{
  "openapi": "3.1.0",
  "info": {
    "title": "Growomat Public API",
    "version": "2.4.1",
    "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. Create a business, then a campaign (it starts `PAUSED`), its ad groups, ads and keywords.\n2. Set the schedule (*Set the ad schedule*), the locations (*Set geo targeting*) and, on more\n   than one platform, the budget split (`_platformBudgets`).\n3. Call *Validate a campaign before export* and fix every `requirement` finding.\n4. Call *Preview a sync plan* to see what would be imported, exported and removed. Keep its\n   `planHash`.\n5. Call *Trigger a sync* with that `planHash`. It returns a `syncId` at once.\n6. Poll *Get a sync* until `effectiveStatus` is `completed`, `partial`, `failed` or `cancelled`.\n7. 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 and download this document. Nothing here needs a scope."
    },
    {
      "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, 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": "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": "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"
      ]
    },
    {
      "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,
            "description": "Target impression share in percent, for `TARGET_IMPRESSION_SHARE`."
          },
          "maxCpcBidCeiling": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Never bid more than this per click."
          }
        }
      },
      "NegativeKeywordEntry": {
        "type": "object",
        "required": [
          "text",
          "matchType"
        ],
        "description": "A search term the campaign must not show for.",
        "properties": {
          "text": {
            "type": "string",
            "description": "The term."
          },
          "matchType": {
            "type": "string",
            "enum": [
              "EXACT",
              "PHRASE",
              "BROAD"
            ],
            "description": "How closely a search must match the term."
          }
        }
      },
      "CampaignTargeting": {
        "type": "object",
        "description": "Additional targeting: schedule, devices and platform-specific audience settings. Locations live in the campaign's `geoTargets` list, not here.",
        "properties": {
          "schedule": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "devices": {
            "type": "object"
          },
          "linkedin": {
            "type": "object"
          }
        }
      },
      "ConversionItem": {
        "type": "object",
        "required": [
          "conversionAction",
          "occurredAt"
        ],
        "description": "One offline conversion. Provide exactly one click id: `gclid`, `gbraid` or `wbraid` for Google Ads, `msclkid` for Microsoft Advertising.",
        "properties": {
          "gclid": {
            "type": "string",
            "description": "Google Ads click id."
          },
          "gbraid": {
            "type": "string",
            "description": "Google Ads click id for app campaigns on iOS."
          },
          "wbraid": {
            "type": "string",
            "description": "Google Ads click id for web campaigns on iOS."
          },
          "msclkid": {
            "type": "string",
            "description": "Microsoft Advertising click id."
          },
          "conversionAction": {
            "type": "string",
            "description": "The conversion action's name or id, for example \"Booked call\"."
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the conversion happened (ISO 8601)."
          },
          "value": {
            "type": "number",
            "description": "Conversion value, optional."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "ISO 4217 currency of `value`, optional."
          },
          "orderId": {
            "type": "string",
            "description": "Idempotency key, for example your order or booking id. The same `orderId` is never counted twice."
          },
          "accountId": {
            "type": "string",
            "description": "Upload to this connected ad account instead of the one inferred from the click id."
          },
          "platformId": {
            "type": "string",
            "enum": [
              "google-ads",
              "microsoft-ads",
              "reddit-ads",
              "meta-ads",
              "openai-ads",
              "linkedin-ads"
            ],
            "description": "Upload to this platform instead of the one inferred from the click id."
          }
        }
      },
      "ConversionUpload": {
        "description": "One conversion, or `{ conversions: [...] }` with up to 200.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/ConversionItem"
          },
          {
            "type": "object",
            "required": [
              "conversions"
            ],
            "properties": {
              "conversions": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ConversionItem"
                },
                "minItems": 1,
                "maxItems": 200
              }
            }
          }
        ]
      },
      "IdentifyRequest": {
        "type": "object",
        "description": "Who a visitor is and which click brought them. Provide an `email` or an `externalId`, ideally with a click id.",
        "properties": {
          "email": {
            "type": "string",
            "description": "The visitor's email. Hashed at rest and used for identity matching."
          },
          "externalId": {
            "type": "string",
            "description": "Your own id for the visitor, for example a CRM or customer id."
          },
          "phone": {
            "type": "string",
            "description": "Phone number in E.164 format. Hashed at rest."
          },
          "gclid": {
            "type": "string",
            "description": "Google Ads click id."
          },
          "gbraid": {
            "type": "string",
            "description": "Google Ads click id for app campaigns on iOS."
          },
          "wbraid": {
            "type": "string",
            "description": "Google Ads click id for web campaigns on iOS."
          },
          "msclkid": {
            "type": "string",
            "description": "Microsoft Advertising click id."
          },
          "clickId": {
            "type": "string",
            "description": "A click id when you do not know which kind it is; set `clickIdType` with it."
          },
          "clickIdType": {
            "type": "string",
            "enum": [
              "gclid",
              "gbraid",
              "wbraid",
              "msclkid"
            ],
            "description": "Which kind of click id `clickId` is."
          },
          "landingUrl": {
            "type": "string",
            "description": "The page the visitor landed on."
          },
          "utm": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "UTM parameters from the landing URL."
          },
          "consent": {
            "$ref": "#/components/schemas/ConsentSignal"
          }
        }
      },
      "ConsentSignal": {
        "type": "object",
        "description": "Consent Mode signals for visitors in the EEA. `UNKNOWN` is sent to the platform as `DENIED`.",
        "properties": {
          "ad_user_data": {
            "type": "string",
            "enum": [
              "GRANTED",
              "DENIED",
              "UNKNOWN"
            ],
            "description": "Whether user data may be used for advertising."
          },
          "ad_personalization": {
            "type": "string",
            "enum": [
              "GRANTED",
              "DENIED",
              "UNKNOWN"
            ],
            "description": "Whether ads may be personalised."
          }
        }
      },
      "ConversionEvent": {
        "type": "object",
        "description": "One conversion event from your own stack. Provide a click id and/or an email so it can be matched to the click or the identity.",
        "properties": {
          "email": {
            "type": "string",
            "description": "The person's email. Hashed at rest."
          },
          "externalId": {
            "type": "string",
            "description": "Your own id for the person."
          },
          "phone": {
            "type": "string",
            "description": "Phone number in E.164 format. Hashed at rest."
          },
          "gclid": {
            "type": "string",
            "description": "Google Ads click id."
          },
          "gbraid": {
            "type": "string",
            "description": "Google Ads click id for app campaigns on iOS."
          },
          "wbraid": {
            "type": "string",
            "description": "Google Ads click id for web campaigns on iOS."
          },
          "msclkid": {
            "type": "string",
            "description": "Microsoft Advertising click id."
          },
          "event": {
            "type": "string",
            "description": "Funnel event name: `signup`, `trial_started`, `qualified_lead`, `paid` or `expansion`. Its conversion action is created on first use."
          },
          "conversionAction": {
            "type": "string",
            "description": "An explicit conversion action name or id, as an alternative to `event`."
          },
          "value": {
            "type": "number",
            "description": "Value of the event."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "ISO 4217 currency of `value`."
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the event happened (ISO 8601)."
          },
          "orderId": {
            "type": "string",
            "description": "Idempotency key, for example a Stripe object id or a CRM deal id."
          },
          "consent": {
            "$ref": "#/components/schemas/ConsentSignal"
          }
        }
      },
      "ConversionEventsRequest": {
        "description": "One event, an array of events, or an object with `events` and/or a provider payload.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/ConversionEvent"
          },
          {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConversionEvent"
            }
          },
          {
            "type": "object",
            "description": "A batch of your own events, or a provider payload that the server normalises according to `source`.",
            "properties": {
              "events": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ConversionEvent"
                }
              },
              "source": {
                "type": "string",
                "enum": [
                  "stripe",
                  "webhook",
                  "hubspot",
                  "salesforce",
                  "ga4"
                ],
                "description": "Which provider `payload` comes from."
              },
              "payload": {
                "type": "object",
                "description": "The provider's own payload, when `source` is set."
              },
              "config": {
                "type": "object",
                "description": "Per-call source settings, such as event or stage mappings."
              }
            }
          }
        ]
      },
      "ConversionAdjustment": {
        "type": "object",
        "required": [
          "adjustmentType",
          "conversionAction"
        ],
        "description": "A correction to a conversion you reported earlier, identified by `orderId` or by `gclid` plus `originalConversionDateTime`.",
        "properties": {
          "adjustmentType": {
            "type": "string",
            "enum": [
              "RETRACTION",
              "RESTATEMENT",
              "ENHANCEMENT"
            ],
            "description": "`RETRACTION` cancels the conversion, `RESTATEMENT` changes its value, `ENHANCEMENT` adds identity data."
          },
          "conversionAction": {
            "type": "string",
            "description": "The conversion action's name or id."
          },
          "orderId": {
            "type": "string",
            "description": "The `orderId` of the original conversion."
          },
          "gclid": {
            "type": "string",
            "description": "Google Ads click id of the original conversion, with `originalConversionDateTime`."
          },
          "msclkid": {
            "type": "string",
            "description": "Microsoft Advertising click id of the original conversion."
          },
          "originalConversionDateTime": {
            "type": "string",
            "format": "date-time",
            "description": "When the original conversion happened."
          },
          "value": {
            "type": "number",
            "description": "The new value, for a `RESTATEMENT`."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "ISO 4217 currency of `value`."
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the adjustment happened. Must not be earlier than the original conversion."
          },
          "consent": {
            "$ref": "#/components/schemas/ConsentSignal"
          }
        }
      },
      "ConversionAdjustmentsRequest": {
        "description": "One adjustment, an array of adjustments, or `{ adjustments: [...] }` with up to 500.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/ConversionAdjustment"
          },
          {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConversionAdjustment"
            }
          },
          {
            "type": "object",
            "required": [
              "adjustments"
            ],
            "properties": {
              "adjustments": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ConversionAdjustment"
                },
                "minItems": 1,
                "maxItems": 500
              }
            }
          }
        ]
      },
      "ConversionSourceCreate": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "A source of conversions to connect to the active Space.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "stripe",
              "webhook",
              "hubspot",
              "salesforce",
              "ga4"
            ],
            "description": "The kind of source."
          },
          "id": {
            "type": "string",
            "description": "Your own id for the source. Defaults to `type`."
          },
          "config": {
            "type": "object",
            "description": "Source settings, such as event or stage mappings."
          },
          "accountRef": {
            "type": "string",
            "description": "Reference to the connected account or secret, managed by the connector."
          }
        }
      },
      "ConversionActionValueSettings": {
        "type": "object",
        "description": "How much a conversion of this action is worth.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "noValue",
              "fixedValue",
              "variableValue"
            ],
            "default": "noValue",
            "description": "`noValue`: count only. `fixedValue`: every conversion is worth `defaultValue`. `variableValue`: each upload carries its own value, with `defaultValue` as the fallback."
          },
          "defaultValue": {
            "type": "number",
            "description": "The fixed or fallback value."
          },
          "currencyCode": {
            "type": "string",
            "description": "ISO 4217 currency of `defaultValue`."
          }
        }
      },
      "ConversionActionCreate": {
        "type": "object",
        "required": [
          "name",
          "type",
          "category"
        ],
        "description": "What counts as a conversion, and how it is counted and valued.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Display name, also accepted wherever a conversion action is referenced by name."
          },
          "type": {
            "type": "string",
            "enum": [
              "WEBPAGE",
              "AD_CALL",
              "WEBSITE_CALL",
              "UPLOAD_CLICKS",
              "UPLOAD_CALLS"
            ],
            "description": "Where the conversion is observed. `UPLOAD_CLICKS` and `UPLOAD_CALLS` are the types for conversions you report through this API."
          },
          "category": {
            "type": "string",
            "enum": [
              "PURCHASE",
              "ADD_TO_CART",
              "BEGIN_CHECKOUT",
              "SUBSCRIBE_PAID",
              "SUBMIT_LEAD_FORM",
              "SIGNUP",
              "CONTACT",
              "BOOK_APPOINTMENT",
              "REQUEST_QUOTE",
              "GET_DIRECTIONS",
              "PAGE_VIEW",
              "DOWNLOAD",
              "PHONE_CALL_LEAD",
              "ENGAGEMENT",
              "DEFAULT"
            ],
            "description": "What kind of outcome this is, as the platforms classify it."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED"
            ],
            "default": "ENABLED",
            "description": "A paused action records nothing."
          },
          "countingType": {
            "type": "string",
            "enum": [
              "ONE_PER_CLICK",
              "MANY_PER_CLICK"
            ],
            "default": "ONE_PER_CLICK",
            "description": "`ONE_PER_CLICK` for leads and sign-ups, `MANY_PER_CLICK` for purchases."
          },
          "valueSettings": {
            "$ref": "#/components/schemas/ConversionActionValueSettings"
          },
          "clickThroughLookbackWindowDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 60,
            "description": "How many days after a click a conversion still counts. Capped at 30 by the platform for all types except calls, where 60 is allowed."
          },
          "viewThroughLookbackWindowDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "description": "How many days after an ad view (without a click) a conversion still counts."
          },
          "attributionModel": {
            "type": "string",
            "enum": [
              "DATA_DRIVEN",
              "LAST_CLICK"
            ],
            "description": "How credit is split across the clicks before a conversion."
          },
          "enhancedConversionsForLeads": {
            "type": "boolean",
            "description": "Enable Enhanced Conversions for Leads, so uploads that carry only a hashed email or phone can still be matched after the click id is gone."
          },
          "leadStage": {
            "type": "string",
            "enum": [
              "signup",
              "trial_started",
              "qualified_lead",
              "paid",
              "expansion"
            ],
            "description": "The funnel stage this action represents. Events reported with this `event` name land here."
          },
          "defaultEventValue": {
            "type": "number",
            "minimum": 0,
            "description": "Value assigned to an event that arrives without one."
          },
          "includeInConversionsMetric": {
            "type": "boolean",
            "description": "Whether the platform counts this action in its headline conversions number and optimises bidding for it."
          }
        }
      },
      "ConversionActionUpdate": {
        "type": "object",
        "description": "Partial update of a conversion action. Same fields as `ConversionActionCreate`, all optional; omitted fields keep their values.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "enum": [
              "WEBPAGE",
              "AD_CALL",
              "WEBSITE_CALL",
              "UPLOAD_CLICKS",
              "UPLOAD_CALLS"
            ]
          },
          "category": {
            "type": "string",
            "enum": [
              "PURCHASE",
              "ADD_TO_CART",
              "BEGIN_CHECKOUT",
              "SUBSCRIBE_PAID",
              "SUBMIT_LEAD_FORM",
              "SIGNUP",
              "CONTACT",
              "BOOK_APPOINTMENT",
              "REQUEST_QUOTE",
              "GET_DIRECTIONS",
              "PAGE_VIEW",
              "DOWNLOAD",
              "PHONE_CALL_LEAD",
              "ENGAGEMENT",
              "DEFAULT"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED"
            ]
          },
          "countingType": {
            "type": "string",
            "enum": [
              "ONE_PER_CLICK",
              "MANY_PER_CLICK"
            ]
          },
          "valueSettings": {
            "$ref": "#/components/schemas/ConversionActionValueSettings"
          },
          "clickThroughLookbackWindowDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 60
          },
          "viewThroughLookbackWindowDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30
          },
          "attributionModel": {
            "type": "string",
            "enum": [
              "DATA_DRIVEN",
              "LAST_CLICK"
            ]
          },
          "enhancedConversionsForLeads": {
            "type": "boolean"
          },
          "leadStage": {
            "type": "string",
            "enum": [
              "signup",
              "trial_started",
              "qualified_lead",
              "paid",
              "expansion"
            ]
          },
          "defaultEventValue": {
            "type": "number",
            "minimum": 0
          },
          "includeInConversionsMetric": {
            "type": "boolean"
          }
        }
      },
      "NativeExtensions": {
        "type": "object",
        "description": "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.\n\nKeys 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`.\n\nDelivery keys read on the campaign: `objective`, `goalType`, `startTime`, `endTime`, `optimizationGoal`, `conversionPixelId`.",
        "additionalProperties": {
          "type": "object",
          "additionalProperties": true
        }
      },
      "PlatformBudgets": {
        "type": "object",
        "description": "Which platforms this campaign runs on, and how much of the budget each one gets, keyed by platform id (`reddit-ads`, `microsoft-ads`, and so on). This is how a campaign is scoped to a platform.\n\n**Absent is not zero.** A campaign with no entries here exports to every connected platform at the full campaign budget: a 30-a-day campaign on three platforms authorises up to 90 a day. Set an entry for each platform you actually want to run on.\n\n`percentage` is computed from `amount` on every write; sending it is ignored. The split is checked against the campaign budget and rejected when it exceeds it.",
        "additionalProperties": {
          "type": "object",
          "required": [
            "amount"
          ],
          "properties": {
            "amount": {
              "type": "number",
              "minimum": 0,
              "description": "This platform's daily share of the budget. `0` is valid: the platform is on but has nothing allocated yet."
            },
            "enabled": {
              "type": "boolean",
              "description": "Delivery on or off for this platform. Absent means on."
            },
            "percentage": {
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "readOnly": true,
              "description": "Share of the campaign budget, computed from `amount`."
            }
          }
        }
      },
      "CampaignCreate": {
        "type": "object",
        "required": [
          "name",
          "budget"
        ],
        "description": "A campaign. Only `name` and `budget` are required; everything else has a sensible default.",
        "properties": {
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "_platformBudgets": {
            "$ref": "#/components/schemas/PlatformBudgets"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Campaign name, shown in the app and on the platforms."
          },
          "budget": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Daily budget, in `currency`."
          },
          "currency": {
            "type": "string",
            "minLength": 1,
            "maxLength": 3,
            "description": "ISO 4217 currency code of `budget`, for example `EUR` or `USD`."
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "PAUSED",
            "description": "`PAUSED` (the default) is safe: the campaign exists on the platforms after a sync but does not serve. `ENABLED` serves after the next sync. `DRAFT` is an unconfirmed build from the app's campaign builder and is never synced until it is confirmed."
          },
          "type": {
            "type": "string",
            "enum": [
              "SEARCH",
              "DISPLAY",
              "VIDEO",
              "SHOPPING",
              "PERFORMANCE_MAX"
            ],
            "default": "SEARCH",
            "description": "The campaign type. It decides which ad formats and which asset roles apply."
          },
          "networks": {
            "type": "string",
            "description": "Platform network setting, as the platform names it."
          },
          "languages": {
            "type": "string",
            "default": "en",
            "description": "Language of the audience, as a language code."
          },
          "businessId": {
            "type": "string",
            "description": "The business this campaign advertises. Generated copy and the brand voice come from it."
          },
          "goal": {
            "type": "string",
            "description": "What the campaign should achieve, in your own words. Used when Growomat drafts ads and keywords for it."
          },
          "geoTargets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GeoTarget"
            },
            "description": "Where the ads may show: named areas and radius circles in one include/exclude list. Empty means no location restriction; the ads can serve anywhere."
          },
          "locationIntent": {
            "type": "string",
            "enum": [
              "PRESENCE",
              "PRESENCE_OR_INTEREST"
            ],
            "default": "PRESENCE",
            "description": "Who counts as being in the targeted places. `PRESENCE` (default): people who are in, or regularly in, those places. `PRESENCE_OR_INTEREST`: also people elsewhere who search about them. The second is wider and much less qualified; it is the platforms' own default, which this API deliberately does not inherit."
          },
          "customLabels": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 50
            },
            "description": "Up to ten free-form labels for your own grouping and reporting."
          },
          "adSchedule": {
            "$ref": "#/components/schemas/CampaignAdScheduleField"
          },
          "adScheduleUseSearcherTimeZone": {
            "type": "boolean",
            "description": "Interpret the ad schedule in the searcher's local time zone instead of the account time zone, on platforms that support it (Microsoft Advertising). Ignored elsewhere."
          },
          "biddingStrategy": {
            "$ref": "#/components/schemas/BiddingStrategy"
          },
          "targeting": {
            "$ref": "#/components/schemas/CampaignTargeting"
          },
          "trackingUrlTemplate": {
            "type": "string",
            "description": "Tracking template applied to every ad in the campaign, for example `{lpurl}?src=ads`."
          },
          "finalUrlSuffix": {
            "type": "string",
            "description": "Query string appended to every final URL in the campaign."
          },
          "urlCustomParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom `{_name}` parameters available to tracking templates."
          },
          "negativeKeywords": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NegativeKeywordEntry"
            },
            "description": "Search terms the whole campaign must not show for."
          }
        }
      },
      "AdGroupAudience": {
        "type": "object",
        "required": [
          "type",
          "id"
        ],
        "description": "An audience the ad group targets or observes, as the platform identifies it.",
        "properties": {
          "type": {
            "type": "string",
            "description": "The platform's audience type."
          },
          "id": {
            "type": "string",
            "description": "The platform's audience id."
          },
          "bidAdjustment": {
            "type": "number",
            "description": "Percent bid adjustment for this audience."
          }
        }
      },
      "AdGroupCreate": {
        "type": "object",
        "required": [
          "campaignId",
          "name"
        ],
        "description": "An ad group inside a campaign.",
        "properties": {
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign this ad group belongs to."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Ad group name."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "A paused ad group serves nothing, whatever its ads say."
          },
          "type": {
            "type": "string",
            "default": "Standard",
            "description": "The platform's ad group type. Leave the default unless the platform requires otherwise."
          },
          "cpc": {
            "type": "number"
          },
          "cpcBid": {
            "type": "number",
            "description": "Default maximum cost per click for the ad group's keywords, in the campaign currency."
          },
          "audiences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdGroupAudience"
            }
          },
          "customLabels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Free-form labels for your own grouping and reporting."
          },
          "trackingUrlTemplate": {
            "type": "string",
            "description": "Tracking template for this ad group; overrides the campaign's."
          },
          "finalUrlSuffix": {
            "type": "string",
            "description": "Query string appended to final URLs; overrides the campaign's."
          },
          "urlCustomParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom `{_name}` parameters available to tracking templates."
          }
        }
      },
      "Sitelink": {
        "type": "object",
        "description": "An extra link shown under the ad.",
        "properties": {
          "text": {
            "type": "string",
            "description": "Link text."
          },
          "url": {
            "type": "string",
            "description": "Where the link goes."
          },
          "description1": {
            "type": "string",
            "description": "First description line."
          },
          "description2": {
            "type": "string",
            "description": "Second description line."
          },
          "trackingUrlTemplate": {
            "type": "string",
            "description": "Tracking template for this link."
          },
          "urlCustomParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom `{_name}` parameters for the tracking template."
          }
        }
      },
      "StructuredSnippet": {
        "type": "object",
        "required": [
          "header",
          "values"
        ],
        "description": "A labelled list shown under the ad, for example \"Services: Repairs, Installation\".",
        "properties": {
          "header": {
            "type": "string",
            "description": "The list label, from the platform's fixed set (for example `Services`, `Brands`)."
          },
          "values": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The list entries."
          }
        }
      },
      "AdCreative": {
        "type": "object",
        "description": "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.",
        "properties": {
          "format": {
            "type": "string",
            "description": "Ad format hint. Normally derived from the campaign type."
          },
          "mediaAssetIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Assets to build the creative from."
          },
          "body": {
            "type": "string",
            "description": "Post body. Defaults to the ad's remaining copy."
          },
          "imageUrl": {
            "type": "string",
            "format": "uri",
            "description": "Image to use, by URL."
          },
          "videoUrl": {
            "type": "string",
            "format": "uri",
            "description": "Video to use, by URL."
          },
          "thumbnailUrl": {
            "type": "string",
            "format": "uri",
            "description": "Still shown before a video plays. Required by platforms that mandate one."
          }
        }
      },
      "ValidateExportRequest": {
        "type": "object",
        "description": "Which platforms to validate against.",
        "properties": {
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Platform ids to validate against, for example `google-ads`. Defaults to every connected platform."
          }
        }
      },
      "Finding": {
        "type": "object",
        "required": [
          "code",
          "level",
          "platformId",
          "capability",
          "messageKey"
        ],
        "description": "One result of validating a campaign against a platform.",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable machine identifier, for example `REDDIT_BID_VALUE_REQUIRED`. Safe to branch on."
          },
          "level": {
            "type": "string",
            "enum": [
              "blocked",
              "unsupported",
              "degraded",
              "ok"
            ],
            "description": "How severe. Findings are sorted worst first."
          },
          "kind": {
            "type": "string",
            "enum": [
              "requirement",
              "transform",
              "limitation",
              "suggestion"
            ],
            "description": "What the finding is, independent of severity. `requirement`: blocks the export until fixed. `transform`: the platform will receive a changed version. `limitation`: a permanent trait of the platform, not fixable. `suggestion`: an optional improvement. Only `requirement` findings mean the campaign needs attention."
          },
          "platformId": {
            "type": "string",
            "description": "The platform this finding is about."
          },
          "capability": {
            "type": "string",
            "description": "The platform capability the finding concerns."
          },
          "capabilityNameKey": {
            "type": "string",
            "description": "Translation key for the capability's display name."
          },
          "entityRef": {
            "type": "object",
            "description": "The entity the finding points at, when it is not the campaign itself.",
            "properties": {
              "type": {
                "type": "string"
              },
              "id": {
                "type": "string"
              }
            }
          },
          "messageKey": {
            "type": "string",
            "description": "Translation key for the user-facing message."
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "description": "Values to fill into the message."
          }
        }
      },
      "AdCreate": {
        "type": "object",
        "required": [
          "adGroupId",
          "campaignId",
          "headlinePart1",
          "headlinePart2",
          "description",
          "finalUrl"
        ],
        "description": "An ad. The headline and description fields describe a responsive search ad; other formats read `creative` and the attached assets.",
        "properties": {
          "angleId": {
            "type": "string",
            "minLength": 1,
            "description": "The angle (claim) this ad argues. Results are reported per angle."
          },
          "conceptId": {
            "type": "string",
            "minLength": 1,
            "description": "The concept (way of making the claim) this ad is a rendition of."
          },
          "angleProvenance": {
            "type": "string",
            "enum": [
              "generated",
              "user",
              "inferred"
            ],
            "description": "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."
          },
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "adGroupId": {
            "type": "string",
            "minLength": 1,
            "description": "The ad group this ad belongs to."
          },
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign of that ad group."
          },
          "type": {
            "type": "string",
            "enum": [
              "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"
            ],
            "default": "RESPONSIVE_SEARCH_AD",
            "description": "Ad format. Defaults from the campaign type per platform; ads imported from a platform record the format that actually serves."
          },
          "creative": {
            "$ref": "#/components/schemas/AdCreative"
          },
          "headlinePart1": {
            "type": "string",
            "description": "First headline. Up to 30 characters on search platforms."
          },
          "headlinePart2": {
            "type": "string",
            "description": "Second headline. Up to 30 characters on search platforms."
          },
          "headlinePart3": {
            "type": "string",
            "description": "Third headline, optional. Up to 30 characters on search platforms."
          },
          "description": {
            "type": "string",
            "description": "First description line. Up to 90 characters on search platforms."
          },
          "description2": {
            "type": "string",
            "description": "Second description line, optional. Up to 90 characters on search platforms."
          },
          "finalUrl": {
            "type": "string",
            "format": "uri",
            "description": "The landing page."
          },
          "path1": {
            "type": "string",
            "maxLength": 15,
            "description": "First display-path segment shown after the domain in the ad."
          },
          "path2": {
            "type": "string",
            "maxLength": 15,
            "description": "Second display-path segment."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "A paused ad is not shown."
          },
          "sitelinks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sitelink"
            },
            "description": "Extra links shown under the ad."
          },
          "callouts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Short phrases shown under the ad, for example \"Free shipping\"."
          },
          "structuredSnippets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StructuredSnippet"
            },
            "description": "Labelled lists shown under the ad."
          },
          "customLabels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Free-form labels for your own grouping and reporting."
          },
          "trackingUrlTemplate": {
            "type": "string",
            "description": "Tracking template for this ad; overrides the ad group's and the campaign's."
          },
          "finalUrlSuffix": {
            "type": "string",
            "description": "Query string appended to the final URL; overrides the ad group's and the campaign's."
          },
          "urlCustomParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom `{_name}` parameters available to tracking templates."
          }
        }
      },
      "KeywordCreate": {
        "type": "object",
        "required": [
          "adGroupId",
          "text"
        ],
        "description": "A keyword in an ad group.",
        "properties": {
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "adGroupId": {
            "type": "string",
            "minLength": 1,
            "description": "The ad group this keyword belongs to."
          },
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign of that ad group. Optional; filled in from the ad group when omitted."
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "description": "The search term."
          },
          "matchType": {
            "type": "string",
            "enum": [
              "BROAD",
              "PHRASE",
              "EXACT"
            ],
            "default": "BROAD",
            "description": "How closely a search must match `text`."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "A paused keyword does not trigger ads."
          },
          "isNegative": {
            "type": "boolean",
            "description": "`true` makes this a negative keyword: searches matching it never show the ad group's ads."
          },
          "cpcBid": {
            "type": [
              "number",
              "null"
            ],
            "description": "Maximum cost per click for this keyword, in the campaign currency. `null` uses the ad group default."
          }
        }
      },
      "SiteLinkEntry": {
        "type": "object",
        "description": "A sitelink the business offers to its ads.",
        "properties": {
          "text": {
            "type": "string",
            "description": "Link text."
          },
          "url": {
            "type": "string",
            "description": "Where the link goes."
          },
          "description1": {
            "type": "string",
            "description": "First description line."
          },
          "description2": {
            "type": "string",
            "description": "Second description line."
          }
        }
      },
      "Brand": {
        "type": "object",
        "description": "The business's brand voice: tone dials, facts the writer may state, and phrases it must never use. It governs every ad and every social post Growomat writes for the business. Over-long values are shortened rather than rejected.",
        "properties": {
          "tone": {
            "type": "object",
            "description": "Two tone dials. Values between 34 and 66 mean \"no strong preference\" and are left out of the writing brief.",
            "properties": {
              "formality": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "default": 50,
                "description": "0 is formal, 100 is relaxed."
              },
              "energy": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "default": 50,
                "description": "0 is matter-of-fact, 100 is enthusiastic."
              }
            }
          },
          "voiceNotes": {
            "type": "string",
            "maxLength": 400,
            "description": "Free text for anything the dials cannot say. Wins over the dials when they conflict."
          },
          "promises": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 80
            },
            "description": "Facts the writer may state without citing a source, for example \"Founded in 1998\"."
          },
          "bannedPhrases": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Words and phrases that must never appear in generated copy. Checked case-insensitively against the drafted text, in each language separately: banning \"cure\" does nothing about \"Heilung\"."
          }
        }
      },
      "BusinessCreate": {
        "type": "object",
        "required": [
          "name",
          "url"
        ],
        "description": "A business: the advertiser its campaigns promote.",
        "properties": {
          "categoryId": {
            "type": "string",
            "description": "The business category, an id from *Get the creative vocabulary* (`businessCategories`). Used to compare results with similar businesses."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Business name."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The business's website: where its ads send people."
          },
          "description": {
            "type": "string",
            "description": "What the business does. Used as context when Growomat writes copy for it."
          },
          "seoKeywords": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "Terms the business wants to be found for, as a comma-separated string or an array."
          },
          "targetGroups": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "Who the business sells to, as a comma-separated string or an array."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "Set to `REMOVED` to archive the business."
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The logo, by URL."
          },
          "logoSource": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "auto-detected",
              "uploaded",
              null
            ],
            "description": "Where the logo came from."
          },
          "siteLinks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SiteLinkEntry"
            },
            "description": "Sitelinks the business offers to its ads."
          },
          "brand": {
            "$ref": "#/components/schemas/Brand"
          }
        }
      },
      "AssetCreate": {
        "type": "object",
        "required": [
          "url"
        ],
        "description": "An asset registered by URL. Growomat stores a pointer to the URL and marks the asset `READY` at once; the bytes are fetched when a sync needs them.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "A public http(s) URL of the image or video."
          },
          "name": {
            "type": "string",
            "maxLength": 255,
            "description": "Display name. Defaults to the file name in the URL."
          },
          "type": {
            "type": "string",
            "enum": [
              "IMAGE",
              "LOGO",
              "VIDEO"
            ],
            "default": "IMAGE",
            "description": "What the asset is used as."
          },
          "mediaType": {
            "type": "string",
            "enum": [
              "IMAGE",
              "VIDEO"
            ],
            "description": "The media kind. Derived from `type` when omitted (`IMAGE` for `IMAGE` and `LOGO`, `VIDEO` for `VIDEO`)."
          },
          "youtubeVideoId": {
            "type": "string",
            "maxLength": 64,
            "description": "For video assets: the YouTube video id. Videos are referenced, never hosted or transcoded by Growomat."
          },
          "contentType": {
            "type": "string",
            "description": "MIME type. Inferred from the URL when omitted."
          },
          "sizeBytes": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "File size in bytes, when known."
          },
          "width": {
            "type": "integer",
            "minimum": 1,
            "description": "Width in pixels, when known."
          },
          "height": {
            "type": "integer",
            "minimum": 1,
            "description": "Height in pixels, when known."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "description": "Free-form tags for finding the asset later."
          },
          "altText": {
            "type": "string",
            "maxLength": 500,
            "description": "Alternative text describing the image."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Anything else you want to keep with the asset."
          }
        }
      },
      "AssetUpdate": {
        "type": "object",
        "description": "The fields of an asset that can change after creation. The URL, content type and size cannot; create a new asset instead.",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255,
            "description": "Display name."
          },
          "altText": {
            "type": "string",
            "maxLength": 500,
            "description": "Alternative text describing the image."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "description": "Free-form tags."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Anything else you want to keep with the asset."
          },
          "status": {
            "type": "string",
            "enum": [
              "PROCESSING",
              "READY",
              "FAILED"
            ],
            "description": "Processing state."
          },
          "width": {
            "type": "integer",
            "minimum": 1,
            "description": "Width in pixels."
          },
          "height": {
            "type": "integer",
            "minimum": 1,
            "description": "Height in pixels."
          }
        }
      },
      "CampaignAssetCreate": {
        "type": "object",
        "required": [
          "campaignId",
          "assetId",
          "role"
        ],
        "description": "Attach an asset to a campaign in a role.",
        "properties": {
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign."
          },
          "assetId": {
            "type": "string",
            "minLength": 1,
            "description": "The asset."
          },
          "role": {
            "type": "string",
            "enum": [
              "MARKETING_IMAGE",
              "SQUARE_MARKETING_IMAGE",
              "PORTRAIT_MARKETING_IMAGE",
              "LOGO",
              "LANDSCAPE_LOGO",
              "YOUTUBE_VIDEO",
              "VIDEO"
            ],
            "description": "What the asset is used as. *Get the media requirements matrix* says which roles each campaign type needs."
          }
        }
      },
      "AdAssetCreate": {
        "type": "object",
        "required": [
          "scope",
          "assetId",
          "role"
        ],
        "description": "Attach an asset to an ad (`scope: AD`, `adId` required) or to an ad group (`scope: AD_GROUP`, `adGroupId` required).",
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "AD",
              "AD_GROUP"
            ],
            "description": "Whether the asset attaches to one ad or to the whole ad group."
          },
          "adId": {
            "type": "string",
            "minLength": 1,
            "description": "The ad. Required when `scope` is `AD`."
          },
          "adGroupId": {
            "type": "string",
            "minLength": 1,
            "description": "The ad group. Required when `scope` is `AD_GROUP`."
          },
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign. Optional; derived from the ad or ad group when omitted."
          },
          "assetId": {
            "type": "string",
            "minLength": 1,
            "description": "The asset."
          },
          "role": {
            "type": "string",
            "enum": [
              "MARKETING_IMAGE",
              "SQUARE_MARKETING_IMAGE",
              "PORTRAIT_MARKETING_IMAGE",
              "LOGO",
              "LANDSCAPE_LOGO",
              "YOUTUBE_VIDEO",
              "VIDEO"
            ],
            "description": "What the asset is used as."
          }
        }
      },
      "AngleCreate": {
        "type": "object",
        "required": [
          "businessId",
          "name",
          "angleType"
        ],
        "description": "An angle is the claim a set of ads argues, and the unit results are decided at. Angles belong to a business: the same claim for two businesses is two angles.",
        "properties": {
          "businessId": {
            "type": "string",
            "minLength": 1,
            "description": "The business the claim is about."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Your own wording of the claim. Never shared outside your Space."
          },
          "angleType": {
            "type": "string",
            "description": "The kind of claim: an id from *Get the creative vocabulary* (`angleTypes`)."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "retired"
            ],
            "default": "active",
            "description": "Retired angles are kept for history but not proposed for new ads."
          }
        }
      },
      "AngleUpdate": {
        "type": "object",
        "description": "Partial update of an angle; omitted fields keep their values. `angleType` must remain a creative-vocabulary id.",
        "properties": {
          "businessId": {
            "type": "string",
            "minLength": 1
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "angleType": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "retired"
            ]
          }
        }
      },
      "ConceptCreate": {
        "type": "object",
        "required": [
          "angleId",
          "name"
        ],
        "description": "A concept is one way of making its angle's claim. Ads reference it through `conceptId`.",
        "properties": {
          "angleId": {
            "type": "string",
            "minLength": 1,
            "description": "The angle this concept belongs to."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Your own name for the concept."
          },
          "attributes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What characterises this way of making the claim: ids from *Get the creative vocabulary* (`conceptAttributes`)."
          }
        }
      },
      "ConceptUpdate": {
        "type": "object",
        "description": "Partial update of a concept; omitted fields keep their values.",
        "properties": {
          "angleId": {
            "type": "string",
            "minLength": 1
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "attributes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "BudgetUpdateRequest": {
        "type": "object",
        "description": "Budget settings. All fields optional; send at least one. `emergency_brake` accepts a bare boolean or `{ \"enabled\": boolean }`.",
        "properties": {
          "global_budget": {
            "type": "number",
            "description": "The budget amount, read according to `period`. Must be positive."
          },
          "period": {
            "type": "string",
            "enum": [
              "daily",
              "monthly"
            ],
            "description": "Whether `global_budget` is a daily or a monthly amount. Monthly is the product default."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency code, for example `EUR`."
          },
          "alert_threshold_percent": {
            "type": "number",
            "minimum": 50,
            "maximum": 100,
            "description": "Percent of the budget at which the \"approaching the limit\" warning is sent. Default 90."
          },
          "emergency_brake": {
            "type": "boolean",
            "description": "Arm (`true`) or disarm (`false`) the Emergency Brake. Armed by default. Disarmed, spending past the budget no longer pauses anything."
          }
        }
      },
      "BrakeReleaseRequest": {
        "type": "object",
        "description": "How to release the brake. An empty body releases the account-level brake in `raise` mode.",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "raise",
              "resume"
            ],
            "default": "raise",
            "description": "`raise` re-arms the brake against the budget's current amount for the rest of the month. `resume` records that you knowingly overruled the budget and leaves that level disarmed until next month."
          },
          "event_id": {
            "type": "string",
            "description": "Release one specific brake event. Defaults to the currently engaged event for `scope`."
          },
          "campaign_id": {
            "type": "string",
            "description": "Release the campaign-level brake of this campaign instead of the account-level one."
          },
          "scope": {
            "type": "string",
            "description": "Which brake: `global` (default) or `campaign:{id}`."
          }
        }
      },
      "SyncTriggerRequest": {
        "type": "object",
        "description": "What to sync. All fields optional; an empty body syncs every connected platform. `campaignIds`, `force`, `confirmFullSpace`, `planHash` and `platformModes` are accepted at the top level or inside `options`.",
        "properties": {
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Platform ids to sync, for example `[\"google-ads\"]`. Defaults to every connected platform."
          },
          "campaignIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Sync only these campaigns, on every platform and in every phase (import, export, remove). The scope is never widened silently: a plan that would touch a campaign outside this list is refused with `409 SYNC_SCOPE_EXCEEDED` unless `confirmFullSpace` is set. Omit to sync the whole Space."
          },
          "force": {
            "type": "boolean",
            "default": false,
            "description": "Re-export the in-scope campaigns even when nothing changed, including targeting, extensions and labels. To recreate a campaign that was deleted on the platform, use *Unlink campaigns from a platform* and then sync."
          },
          "confirmFullSpace": {
            "type": "boolean",
            "default": false,
            "description": "Acknowledge a large change. Required when an unscoped sync would change more than 10 campaigns, and to override a `SYNC_SCOPE_EXCEEDED` refusal."
          },
          "planHash": {
            "type": "string",
            "description": "Run exactly the plan you previewed: pass the `planHash` from *Preview a sync plan*. If the plan has changed since, the sync is refused with `409 SYNC_PLAN_DRIFT` instead of running something you did not review."
          },
          "platformModes": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "import",
                  "export",
                  "remove"
                ]
              }
            },
            "description": "Per-platform direction: map a platform id to the phases it may run. A platform absent from the map runs all three. Combines with `campaignIds` (which campaigns) and `planHash` (the hash covers the full previewed plan; holding a direction back is a choice, not drift). Work held back is reported in the response under `scope.excludedByMode`. Example, to keep importing from a platform while holding its exports: `{\"google-ads\": [\"import\", \"remove\"]}`. Unknown platform ids or directions fail with `400 INVALID_PLATFORM_MODES`."
          },
          "options": {
            "type": "object",
            "description": "Which entity types to include. Also accepts `force`, `campaignIds`, `confirmFullSpace` and `planHash`.",
            "properties": {
              "includeAdGroups": {
                "type": "boolean",
                "default": true,
                "description": "Sync ad groups."
              },
              "includeKeywords": {
                "type": "boolean",
                "default": true,
                "description": "Sync keywords."
              },
              "includeAds": {
                "type": "boolean",
                "default": true,
                "description": "Sync ads."
              },
              "force": {
                "type": "boolean",
                "default": false,
                "description": "Same as the top-level `force`."
              },
              "campaignIds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Same as the top-level `campaignIds`."
              }
            }
          }
        }
      },
      "SyncUnlinkRequest": {
        "type": "object",
        "required": [
          "platform"
        ],
        "description": "Forget the link between local campaigns and their copies on one platform. The local campaigns are kept; the next sync re-creates them on the platform.",
        "properties": {
          "platform": {
            "type": "string",
            "description": "Platform id, for example `google-ads`."
          },
          "campaignIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Campaigns to unlink. Omit to unlink every campaign linked to the platform."
          },
          "deleteRemote": {
            "type": "boolean",
            "default": false,
            "description": "Also remove the campaign on the platform. A campaign that is already gone there counts as removed. When `false`, only the local link is cleared and the platform copy is left untouched."
          }
        }
      },
      "SyncTriggerResponse": {
        "type": "object",
        "description": "What *Trigger a sync* returns.",
        "properties": {
          "syncId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id to poll with *Get a sync*. `null` when there was nothing to sync."
          },
          "status": {
            "type": "string",
            "enum": [
              "started",
              "nothing_to_sync"
            ],
            "description": "Whether a sync was started."
          },
          "spaceId": {
            "type": "string",
            "description": "The Space that was synced."
          },
          "plan": {
            "$ref": "#/components/schemas/SyncPlan"
          },
          "planHash": {
            "type": "string",
            "description": "Content hash of the plan that was executed, the same value *Preview a sync plan* returns. Keep it for audit."
          },
          "scope": {
            "type": "object",
            "description": "The scope that was actually executed, echoed back so that an ignored or mistyped `campaignIds` is visible rather than silent.",
            "properties": {
              "requestedCampaignIds": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "string"
                },
                "description": "The `campaignIds` you sent, or `null` for the whole Space."
              },
              "platforms": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The platforms synced."
              },
              "force": {
                "type": "boolean",
                "description": "Whether unchanged campaigns were re-exported."
              },
              "platformModes": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "description": "The per-platform directions this run honoured, or `null`."
              },
              "excludedByMode": {
                "type": "object",
                "additionalProperties": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer"
                  }
                },
                "description": "Work held back by `platformModes`, per platform and phase, so a withheld export can be told apart from \"nothing needed exporting\"."
              },
              "plannedCampaignIds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The campaigns the plan covered."
              },
              "plannedImports": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Remote campaigns the plan imported."
              },
              "totalOps": {
                "type": "integer",
                "description": "Total operations in the plan."
              }
            }
          }
        }
      },
      "SyncCampaignDetail": {
        "type": "object",
        "description": "Progress of one campaign within a sync.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The campaign id."
          },
          "name": {
            "type": "string",
            "description": "The campaign name."
          },
          "platform": {
            "type": "string",
            "description": "The platform this entry is about."
          },
          "phase": {
            "type": "string",
            "enum": [
              "remove",
              "export",
              "import"
            ],
            "description": "Which phase this entry belongs to."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "in_progress",
              "completed",
              "failed",
              "skipped",
              "partial",
              "unknown"
            ],
            "description": "How far this campaign got."
          },
          "error": {
            "type": "string",
            "description": "What went wrong. Present when `status` is `failed` or `partial`."
          },
          "errorCode": {
            "type": "string",
            "description": "Machine-readable failure code, for example `CHILD_EXPORT_FAILED` or `CRITERIA_SYNC_FAILED`."
          },
          "errorDetails": {
            "type": "array",
            "description": "The platform's own error records, one per failed operation (for example a policy name and the field it applies to), so a `partial` or `failed` campaign can be diagnosed from this response alone.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "processedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When this campaign was processed."
          }
        }
      },
      "SyncProgress": {
        "type": "object",
        "description": "Where a running sync is.",
        "properties": {
          "currentPlatform": {
            "type": "string",
            "description": "The platform being processed."
          },
          "currentPhase": {
            "type": "string",
            "description": "The phase being processed."
          },
          "totalCampaigns": {
            "type": "integer",
            "description": "Campaigns in the plan."
          },
          "processedCampaigns": {
            "type": "integer",
            "description": "Campaigns finished so far."
          },
          "campaignDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SyncCampaignDetail"
            },
            "description": "One entry per campaign, platform and phase."
          }
        }
      },
      "SyncTotals": {
        "type": "object",
        "description": "What a sync touched.",
        "properties": {
          "campaigns": {
            "type": "integer",
            "description": "Campaigns processed."
          },
          "adGroups": {
            "type": "integer",
            "description": "Ad groups processed."
          },
          "keywords": {
            "type": "integer",
            "description": "Keywords processed."
          },
          "ads": {
            "type": "integer",
            "description": "Ads processed."
          },
          "conversionActions": {
            "type": "integer",
            "description": "Conversion actions processed."
          },
          "removals": {
            "type": "integer",
            "description": "Campaigns removed from a platform."
          },
          "errors": {
            "type": "integer",
            "description": "Failed campaign operations. Any value above zero makes the sync `partial`."
          }
        }
      },
      "SyncPlan": {
        "type": "object",
        "description": "What a sync would do, per platform: how many campaigns to import, export and remove.",
        "properties": {
          "platforms": {
            "type": "object",
            "description": "Keyed by platform id.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "totals": {
                  "type": "object",
                  "properties": {
                    "import": {
                      "type": "integer"
                    },
                    "export": {
                      "type": "integer"
                    },
                    "remove": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SyncRecord": {
        "type": "object",
        "description": "One sync, from trigger to finish. Final `status` values are `completed`, `partial`, `failed` and `cancelled`.",
        "properties": {
          "syncId": {
            "type": "string",
            "description": "The sync id."
          },
          "spaceId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The Space that was synced."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "in_progress",
              "completed",
              "partial",
              "failed",
              "cancelled",
              "paused"
            ],
            "description": "The raw status."
          },
          "stalled": {
            "type": "boolean",
            "description": "`true` when a sync that has not finished has shown no activity for more than ten minutes. It is presumed hung and will be closed automatically."
          },
          "effectiveStatus": {
            "type": "string",
            "description": "The status to act on: `stalled` when hung, otherwise the same as `status`. Poll until it is `completed`, `partial`, `failed` or `cancelled`."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the sync started."
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it finished, or `null` while running."
          },
          "scheduled": {
            "type": "boolean",
            "description": "`true` for a sync started by the schedule rather than by a request."
          },
          "platforms": {
            "type": "object",
            "additionalProperties": true,
            "description": "Per-platform results."
          },
          "totals": {
            "$ref": "#/components/schemas/SyncTotals"
          },
          "progress": {
            "$ref": "#/components/schemas/SyncProgress"
          },
          "errors": {
            "type": "array",
            "description": "Sync-level errors, in the order they happened.",
            "items": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string"
                },
                "timestamp": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "CampaignUpdate": {
        "type": "object",
        "description": "Partial update of a campaign: any subset of `CampaignCreate` fields. Omitted fields keep their values.",
        "properties": {
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "_platformBudgets": {
            "$ref": "#/components/schemas/PlatformBudgets"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Campaign name, shown in the app and on the platforms."
          },
          "budget": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Daily budget, in `currency`."
          },
          "currency": {
            "type": "string",
            "minLength": 1,
            "maxLength": 3,
            "description": "ISO 4217 currency code of `budget`, for example `EUR` or `USD`."
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "PAUSED",
            "description": "`PAUSED` (the default) is safe: the campaign exists on the platforms after a sync but does not serve. `ENABLED` serves after the next sync. `DRAFT` is an unconfirmed build from the app's campaign builder and is never synced until it is confirmed."
          },
          "type": {
            "type": "string",
            "enum": [
              "SEARCH",
              "DISPLAY",
              "VIDEO",
              "SHOPPING",
              "PERFORMANCE_MAX"
            ],
            "default": "SEARCH",
            "description": "The campaign type. It decides which ad formats and which asset roles apply."
          },
          "networks": {
            "type": "string",
            "description": "Platform network setting, as the platform names it."
          },
          "languages": {
            "type": "string",
            "default": "en",
            "description": "Language of the audience, as a language code."
          },
          "businessId": {
            "type": "string",
            "description": "The business this campaign advertises. Generated copy and the brand voice come from it."
          },
          "goal": {
            "type": "string",
            "description": "What the campaign should achieve, in your own words. Used when Growomat drafts ads and keywords for it."
          },
          "geoTargets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GeoTarget"
            },
            "description": "Where the ads may show: named areas and radius circles in one include/exclude list. Empty means no location restriction; the ads can serve anywhere."
          },
          "locationIntent": {
            "type": "string",
            "enum": [
              "PRESENCE",
              "PRESENCE_OR_INTEREST"
            ],
            "default": "PRESENCE",
            "description": "Who counts as being in the targeted places. `PRESENCE` (default): people who are in, or regularly in, those places. `PRESENCE_OR_INTEREST`: also people elsewhere who search about them. The second is wider and much less qualified; it is the platforms' own default, which this API deliberately does not inherit."
          },
          "customLabels": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 50
            },
            "description": "Up to ten free-form labels for your own grouping and reporting."
          },
          "adSchedule": {
            "$ref": "#/components/schemas/CampaignAdScheduleField"
          },
          "adScheduleUseSearcherTimeZone": {
            "type": "boolean",
            "description": "Interpret the ad schedule in the searcher's local time zone instead of the account time zone, on platforms that support it (Microsoft Advertising). Ignored elsewhere."
          },
          "biddingStrategy": {
            "$ref": "#/components/schemas/BiddingStrategy"
          },
          "targeting": {
            "$ref": "#/components/schemas/CampaignTargeting"
          },
          "trackingUrlTemplate": {
            "type": "string",
            "description": "Tracking template applied to every ad in the campaign, for example `{lpurl}?src=ads`."
          },
          "finalUrlSuffix": {
            "type": "string",
            "description": "Query string appended to every final URL in the campaign."
          },
          "urlCustomParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom `{_name}` parameters available to tracking templates."
          },
          "negativeKeywords": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NegativeKeywordEntry"
            },
            "description": "Search terms the whole campaign must not show for."
          }
        }
      },
      "AdGroupUpdate": {
        "type": "object",
        "description": "Partial update of an ad group: any subset of `AdGroupCreate` fields. Omitted fields keep their values.",
        "properties": {
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign this ad group belongs to."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Ad group name."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "A paused ad group serves nothing, whatever its ads say."
          },
          "type": {
            "type": "string",
            "default": "Standard",
            "description": "The platform's ad group type. Leave the default unless the platform requires otherwise."
          },
          "cpc": {
            "type": "number"
          },
          "cpcBid": {
            "type": "number",
            "description": "Default maximum cost per click for the ad group's keywords, in the campaign currency."
          },
          "audiences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdGroupAudience"
            }
          },
          "customLabels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Free-form labels for your own grouping and reporting."
          },
          "trackingUrlTemplate": {
            "type": "string",
            "description": "Tracking template for this ad group; overrides the campaign's."
          },
          "finalUrlSuffix": {
            "type": "string",
            "description": "Query string appended to final URLs; overrides the campaign's."
          },
          "urlCustomParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom `{_name}` parameters available to tracking templates."
          }
        }
      },
      "AdUpdate": {
        "type": "object",
        "description": "Partial update of an ad: any subset of `AdCreate` fields. Omitted fields keep their values.",
        "properties": {
          "angleId": {
            "type": "string",
            "minLength": 1,
            "description": "The angle (claim) this ad argues. Results are reported per angle."
          },
          "conceptId": {
            "type": "string",
            "minLength": 1,
            "description": "The concept (way of making the claim) this ad is a rendition of."
          },
          "angleProvenance": {
            "type": "string",
            "enum": [
              "generated",
              "user",
              "inferred"
            ],
            "description": "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."
          },
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "adGroupId": {
            "type": "string",
            "minLength": 1,
            "description": "The ad group this ad belongs to."
          },
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign of that ad group."
          },
          "type": {
            "type": "string",
            "enum": [
              "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"
            ],
            "default": "RESPONSIVE_SEARCH_AD",
            "description": "Ad format. Defaults from the campaign type per platform; ads imported from a platform record the format that actually serves."
          },
          "creative": {
            "$ref": "#/components/schemas/AdCreative"
          },
          "headlinePart1": {
            "type": "string",
            "description": "First headline. Up to 30 characters on search platforms."
          },
          "headlinePart2": {
            "type": "string",
            "description": "Second headline. Up to 30 characters on search platforms."
          },
          "headlinePart3": {
            "type": "string",
            "description": "Third headline, optional. Up to 30 characters on search platforms."
          },
          "description": {
            "type": "string",
            "description": "First description line. Up to 90 characters on search platforms."
          },
          "description2": {
            "type": "string",
            "description": "Second description line, optional. Up to 90 characters on search platforms."
          },
          "finalUrl": {
            "type": "string",
            "format": "uri",
            "description": "The landing page."
          },
          "path1": {
            "type": "string",
            "maxLength": 15,
            "description": "First display-path segment shown after the domain in the ad."
          },
          "path2": {
            "type": "string",
            "maxLength": 15,
            "description": "Second display-path segment."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "A paused ad is not shown."
          },
          "sitelinks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sitelink"
            },
            "description": "Extra links shown under the ad."
          },
          "callouts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Short phrases shown under the ad, for example \"Free shipping\"."
          },
          "structuredSnippets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StructuredSnippet"
            },
            "description": "Labelled lists shown under the ad."
          },
          "customLabels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Free-form labels for your own grouping and reporting."
          },
          "trackingUrlTemplate": {
            "type": "string",
            "description": "Tracking template for this ad; overrides the ad group's and the campaign's."
          },
          "finalUrlSuffix": {
            "type": "string",
            "description": "Query string appended to the final URL; overrides the ad group's and the campaign's."
          },
          "urlCustomParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom `{_name}` parameters available to tracking templates."
          }
        }
      },
      "KeywordUpdate": {
        "type": "object",
        "description": "Partial update of a keyword: any subset of `KeywordCreate` fields. Omitted fields keep their values.",
        "properties": {
          "_native": {
            "$ref": "#/components/schemas/NativeExtensions"
          },
          "adGroupId": {
            "type": "string",
            "minLength": 1,
            "description": "The ad group this keyword belongs to."
          },
          "campaignId": {
            "type": "string",
            "minLength": 1,
            "description": "The campaign of that ad group. Optional; filled in from the ad group when omitted."
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "description": "The search term."
          },
          "matchType": {
            "type": "string",
            "enum": [
              "BROAD",
              "PHRASE",
              "EXACT"
            ],
            "default": "BROAD",
            "description": "How closely a search must match `text`."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "PAUSED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "A paused keyword does not trigger ads."
          },
          "isNegative": {
            "type": "boolean",
            "description": "`true` makes this a negative keyword: searches matching it never show the ad group's ads."
          },
          "cpcBid": {
            "type": [
              "number",
              "null"
            ],
            "description": "Maximum cost per click for this keyword, in the campaign currency. `null` uses the ad group default."
          }
        }
      },
      "BusinessUpdate": {
        "type": "object",
        "description": "Partial update of a business: any subset of `BusinessCreate` fields. Omitted fields keep their values.",
        "properties": {
          "categoryId": {
            "type": "string",
            "description": "The business category, an id from *Get the creative vocabulary* (`businessCategories`). Used to compare results with similar businesses."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Business name."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The business's website: where its ads send people."
          },
          "description": {
            "type": "string",
            "description": "What the business does. Used as context when Growomat writes copy for it."
          },
          "seoKeywords": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "Terms the business wants to be found for, as a comma-separated string or an array."
          },
          "targetGroups": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "Who the business sells to, as a comma-separated string or an array."
          },
          "status": {
            "type": "string",
            "enum": [
              "ENABLED",
              "REMOVED"
            ],
            "default": "ENABLED",
            "description": "Set to `REMOVED` to archive the business."
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The logo, by URL."
          },
          "logoSource": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "auto-detected",
              "uploaded",
              null
            ],
            "description": "Where the logo came from."
          },
          "siteLinks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SiteLinkEntry"
            },
            "description": "Sitelinks the business offers to its ads."
          },
          "brand": {
            "$ref": "#/components/schemas/Brand"
          }
        }
      },
      "ContentDraftCreate": {
        "type": "object",
        "required": [
          "goal",
          "businessId"
        ],
        "description": "A new piece of social content to draft.",
        "properties": {
          "goal": {
            "type": "string",
            "minLength": 4,
            "description": "What the piece should say: the idea that is researched and drafted."
          },
          "businessId": {
            "type": "string",
            "description": "The business the piece is written for. Its brand voice applies."
          },
          "researchEnabled": {
            "type": "boolean",
            "default": true,
            "description": "Ground the piece in web research. When off, no sources are attached and no source sign-off is needed."
          },
          "recencyDays": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3650,
            "default": 90,
            "description": "Only use sources newer than this many days."
          },
          "overrides": {
            "type": "object",
            "description": "Settings pinned from the start: `platform`, `blueprint`, `audience`, `voice`. Pinned fields are never changed during drafting. Unknown keys and invalid values are dropped.",
            "properties": {
              "platform": {
                "type": "string",
                "description": "Where the piece will be published, for example a social network or a blog."
              },
              "blueprint": {
                "type": "string",
                "description": "The piece blueprint id."
              },
              "audience": {
                "type": "string",
                "maxLength": 200,
                "description": "Who the piece is for."
              },
              "voice": {
                "type": "string",
                "maxLength": 200,
                "description": "Tone notes for this piece, on top of the brand voice."
              }
            }
          }
        }
      },
      "ContentDraftAnswer": {
        "type": "object",
        "required": [
          "questionId"
        ],
        "description": "An answer to one interview question.",
        "properties": {
          "questionId": {
            "type": "string",
            "description": "The id of an entry in the draft's `openQuestions`."
          },
          "answer": {
            "description": "The answer. Its type follows the question's `kind`: a boolean, a single choice, or free text.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              }
            ]
          }
        }
      },
      "ContentDraftConfirm": {
        "type": "object",
        "description": "Confirm options. An empty body is valid.",
        "properties": {
          "skipRewrite": {
            "type": "boolean",
            "default": false,
            "description": "Finish without rewriting even when interview answers changed after the piece was written, for example when the monthly draft allowance is spent. The answers are recorded as applied so the piece does not stay pending."
          }
        }
      }
    }
  },
  "security": [
    {
      "ApiToken": []
    }
  ],
  "paths": {
    "/api/v1/me": {
      "get": {
        "operationId": "me_get",
        "summary": "Introspect the current token",
        "description": "Returns who the token belongs to and what it may do:\n\n- `uid` and `email`: the account the token was created in.\n- `plan`: `planId`, `state`, `trialEndsAt` and `currentPeriodEnd` of the subscription.\n- `scopes`: the scopes this token was granted.\n- `scheduling.timezones`: the account time zone per connected platform, which is what ad-schedule hours are anchored to.\n\nCall this first to confirm a token works and to learn its scopes before planning writes. Needs no scope and ignores `X-Space-Id`.",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": []
          }
        ]
      }
    },
    "/api/v1/spaces": {
      "get": {
        "operationId": "spaces_list",
        "summary": "List your Spaces",
        "description": "Every Space the token owner belongs to, including the personal Space (`type: personal`, `role: owner`). Each row carries `id`, `name`, `slug`, `icon`, `color`, `type`, `role`, `memberCount` and `campaignCount`.\n\nUse a row's `id` (`spc_…`) as the `X-Space-Id` header to work inside that Space. Needs no scope.",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": []
          }
        ]
      }
    },
    "/api/v1/openapi.json": {
      "get": {
        "operationId": "openapi_get",
        "summary": "Download this OpenAPI document",
        "description": "The OpenAPI 3.1 document for `/api/v1`, exactly as published. No authentication required. Generate a client from it, or hand it to an agent that plans its own calls.",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/campaigns": {
      "get": {
        "operationId": "campaigns_list",
        "summary": "List campaigns",
        "description": "Campaigns in the active Space. Every campaign carries its delivery status:\n\n- `delivery`: one entry per platform the campaign runs on, with `platform`, `state`, the `reasons[]` behind that state (cause first), `since` and `updatedAt`.\n- `deliverySummary`: the worst of those states, with its `platform` and `reasons`. This is the answer to \"is it serving, and if not, why?\". `null` until the campaign has been synced.\n\nStates: `SERVING`, `LEARNING`, `LIMITED_BUDGET`, `NOT_SERVING`, `NOT_LIVE_YET`, `NOT_EXPORTED`, `PAUSED`, `ENDED`. A campaign that has never been synced is `NOT_EXPORTED`.",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "campaigns_create",
        "summary": "Create a campaign",
        "description": "Creates a campaign in the active Space. New campaigns start `PAUSED` and stay local until a sync pushes them to a platform, so creating one never spends money.\n\nRequired: `name` and `budget` (a daily amount in `currency`). Set `businessId` to link the campaign to a business, and `_platformBudgets` to choose which platforms it runs on; with no split it exports to every connected platform at the full budget.\n\n`adSchedule` accepts a preset name or a structured weekly schedule (see `CampaignAdScheduleField`). For human-friendly schedule input use *Set the ad schedule* after creating.\n\nThe response is the stored campaign, including `delivery` and `deliverySummary`.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.\n- `400 AD_SCHEDULE_TOO_MANY_WINDOWS`: more than 84 schedule windows.",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/campaigns/{id}": {
      "get": {
        "operationId": "campaigns_get",
        "summary": "Get a campaign",
        "description": "Returns the campaign with everything needed to act on it.\n\n- `delivery`: one entry per platform the campaign runs on, with its `state`, the `reasons[]` behind that state (cause first), and `since`.\n- `deliverySummary`: the worst of those states, with its platform and reasons. This is the answer to \"is it serving, and if not, why?\".\n- `adScheduleStructured`: the weekly schedule as stored (`windows[]` plus `timezone`), present once a schedule has been set.\n- `adSchedule`: the schedule as you last wrote it, either a preset name or the structured object.\n- `_platformBudgets`: the per-platform delivery split, so you can read back the scope you set.\n\nStates: `SERVING`, `LEARNING`, `LIMITED_BUDGET`, `NOT_SERVING`, `NOT_LIVE_YET`, `NOT_EXPORTED`, `PAUSED`, `ENDED`. A campaign that has never been synced is `NOT_EXPORTED`.\n\n**Errors**\n\n- `404 NOT_FOUND`: no campaign with this id in the active Space.",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The campaign id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "campaigns_update",
        "summary": "Update a campaign",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included).\n\n`adSchedule` accepts a preset name (`ALL_HOURS_ALL_DAYS`, `BUSINESS_HOURS`) or a structured schedule; `adScheduleStructured` is recomputed on every write. For human-friendly input (`09:00` to `17:00`, weekdays, a time zone) use *Set the ad schedule* instead.\n\nChanges reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no campaign with this id in the active Space.\n- `409 CAMPAIGN_IS_DRAFT`: the campaign is an unconfirmed draft; it can only be confirmed in the app.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.\n- `400 AD_SCHEDULE_TOO_MANY_WINDOWS`: more than 84 schedule windows.",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The campaign id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "campaigns_delete",
        "summary": "Delete a campaign",
        "description": "A campaign that has never been pushed to a platform is deleted immediately. A campaign that is linked to a platform is marked for removal instead and disappears from the platform on the next sync, so the remote campaign is never orphaned.\n\nResponse: `{ id, deleted, deferred, platforms }`. `deleted: true` means it is gone now; `deferred: true` means the removal happens on the next sync, and `platforms` lists where.\n\n**Errors**\n\n- `404 NOT_FOUND`: no campaign with this id in the active Space.",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The campaign id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/campaigns/{id}/validate-export": {
      "post": {
        "operationId": "campaigns_validate_export",
        "summary": "Validate a campaign before export",
        "description": "Runs the same checks a sync runs before it exports, without calling any platform. Use it before *Trigger a sync* so a refusal shows up here rather than inside a sync record.\n\nReturns `valid` (true when nothing blocks the export), `platforms` (the ids checked) and `findings[]`, ordered worst first: `blocked`, `unsupported`, `degraded`, `ok`. Each finding also has a `kind` that says what it *is*:\n\n- `requirement`: something you must fix before this platform accepts the campaign.\n- `transform`: the platform will receive a changed version (a clamped value, for example).\n- `limitation`: a permanent trait of the platform; nothing to fix.\n- `suggestion`: an optional improvement.\n\nOnly `requirement` findings need your attention. Body `platforms` defaults to every connected platform; naming a platform validates against it even when it is not connected.\n\n**Errors**\n\n- `404 NOT_FOUND`: no campaign with this id in the active Space.\n- `400 UNKNOWN_PLATFORM`: a platform id in the body does not exist.",
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The campaign id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValidateExportRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/campaigns/{id}/ad-schedule": {
      "post": {
        "operationId": "campaigns_set_ad_schedule",
        "summary": "Set the ad schedule",
        "description": "Human-friendly schedule input: `HH:MM` windows, day shorthands (`MON`, `WEEKDAYS`, `WEEKEND`) and presets. The server expands them, splits windows that cross midnight, resolves the time zone and stores the result as `adScheduleStructured`.\n\nTime zones: `\"local\"` means the searcher's own time zone on platforms that support it, and the account time zone elsewhere. An explicit IANA zone (`Europe/Berlin`) is converted into account-time hours before storing. *Introspect the current token* shows the account time zone per platform under `scheduling.timezones`.\n\nPass `dry_run: true` to get the expanded schedule and any warnings without changing the campaign. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no campaign with this id in the active Space.\n- `400 INVALID_REQUEST`: the body is not valid JSON.\n- `400` with a schedule-specific `error.code` (for example `AD_SCHEDULE_TOO_MANY_WINDOWS`), or `VALIDATION_ERROR` with `details.issues[]`.",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The campaign id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetAdSchedule"
              }
            }
          }
        }
      }
    },
    "/api/v1/campaigns/{id}/geo-targeting": {
      "post": {
        "operationId": "campaigns_set_geo_targeting",
        "summary": "Set geo targeting",
        "description": "One call for the whole location set. Send either the structured `geoTargets` array, or friendly `include`, `exclude` and `radii` lists of place names that the server resolves for you, plus the campaign-level `locationIntent` (who counts as being in a place).\n\nRules:\n\n- An empty `geoTargets` is valid and means \"serve everywhere\".\n- A radius circle cannot be excluded; exclude a named area instead.\n- A set made only of exclusions is rejected.\n\nPass `dry_run: true` to get the resolved rows and a plain-language sentence describing the effective targeting, without changing the campaign. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no campaign with this id in the active Space.\n- `400 RADIUS_EXCLUSION_UNSUPPORTED`: a radius row was marked `negative`.\n- `400 ONLY_EXCLUSIONS`: every row is an exclusion.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The campaign id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetGeoTargeting"
              }
            }
          }
        }
      }
    },
    "/api/v1/ad-groups": {
      "get": {
        "operationId": "adGroups_list",
        "summary": "List ad groups",
        "description": "Ad groups in the active Space, across all campaigns.",
        "tags": [
          "ad-groups"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "adGroups_create",
        "summary": "Create an ad group",
        "description": "Creates an ad group inside a campaign (`campaignId` required). Ad groups hold the ads and keywords; `cpcBid` sets the default maximum cost per click for its keywords.\n\nChanges reach the ad platforms on the next sync.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "ad-groups"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdGroupCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/ad-groups/{id}": {
      "get": {
        "operationId": "adGroups_get",
        "summary": "Get an ad group",
        "description": "Returns one ad group.\n\n**Errors**\n\n- `404 NOT_FOUND`: no ad group with this id in the active Space.",
        "tags": [
          "ad-groups"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The ad group id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "adGroups_update",
        "summary": "Update an ad group",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included). Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no ad group with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "ad-groups"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The ad group id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdGroupUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "adGroups_delete",
        "summary": "Delete an ad group",
        "description": "Deletes an empty ad group. While it still has ads or keywords the request is refused, so nothing disappears by accident; pass `?cascade=true` to delete them in the same request. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no ad group with this id in the active Space.\n- `409 CONFLICT`: the ad group still has ads or keywords; `details` carries the counts (`{ ads, keywords }`).",
        "tags": [
          "ad-groups"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The ad group id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cascade",
            "in": "query",
            "required": false,
            "description": "Set to `true` to delete the ad group's ads and keywords along with it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/ads": {
      "get": {
        "operationId": "ads_list",
        "summary": "List ads",
        "description": "Ads in the active Space. Filter by `campaignId` or `adGroupId` to scope the list on the server.",
        "tags": [
          "ads"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "campaignId",
            "in": "query",
            "required": false,
            "description": "Only return ads in this campaign.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "adGroupId",
            "in": "query",
            "required": false,
            "description": "Only return ads in this ad group.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "ads_create",
        "summary": "Create an ad",
        "description": "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.\n\nAn ad is shown only once its campaign has been synced and is serving. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "ads"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/ads/{id}": {
      "get": {
        "operationId": "ads_get",
        "summary": "Get an ad",
        "description": "Returns one ad, including its platform approval status where known.\n\n**Errors**\n\n- `404 NOT_FOUND`: no ad with this id in the active Space.",
        "tags": [
          "ads"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The ad id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "ads_update",
        "summary": "Update an ad",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included). Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no ad with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "ads"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The ad id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "ads_delete",
        "summary": "Delete an ad",
        "description": "Deletes the ad. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no ad with this id in the active Space.",
        "tags": [
          "ads"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The ad id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/keywords": {
      "get": {
        "operationId": "keywords_list",
        "summary": "List keywords",
        "description": "Keywords in the active Space. Filter by `adGroupId` or `campaignId` to scope the list on the server.",
        "tags": [
          "keywords"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "keywords:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "adGroupId",
            "in": "query",
            "required": false,
            "description": "Only return keywords in this ad group.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaignId",
            "in": "query",
            "required": false,
            "description": "Only return keywords in this campaign.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "keywords_create",
        "summary": "Create a keyword",
        "description": "Adds a keyword to an ad group. `matchType` defaults to `BROAD`; set `isNegative: true` for a negative keyword. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "keywords"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "keywords:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeywordCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/keywords/{id}": {
      "get": {
        "operationId": "keywords_get",
        "summary": "Get a keyword",
        "description": "Returns one keyword.\n\n**Errors**\n\n- `404 NOT_FOUND`: no keyword with this id in the active Space.",
        "tags": [
          "keywords"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "keywords:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The keyword id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "keywords_update",
        "summary": "Update a keyword",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included). Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no keyword with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "keywords"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "keywords:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The keyword id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeywordUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "keywords_delete",
        "summary": "Delete a keyword",
        "description": "Deletes the keyword. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no keyword with this id in the active Space.",
        "tags": [
          "keywords"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "keywords:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The keyword id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/businesses": {
      "get": {
        "operationId": "businesses_list",
        "summary": "List businesses",
        "description": "Businesses in the active Space.",
        "tags": [
          "businesses"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "businesses:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "businesses_create",
        "summary": "Create a business",
        "description": "A business is the advertiser: the website its ads send people to, what it does, and its brand voice. Campaigns link to it through `businessId`, and generated ad copy is written for it. Required: `name` and `url`.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "businesses"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "businesses:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BusinessCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/businesses/{id}": {
      "get": {
        "operationId": "businesses_get",
        "summary": "Get a business",
        "description": "Returns one business.\n\n**Errors**\n\n- `404 NOT_FOUND`: no business with this id in the active Space.",
        "tags": [
          "businesses"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "businesses:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The business id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "businesses_update",
        "summary": "Update a business",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included).\n\n**Errors**\n\n- `404 NOT_FOUND`: no business with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "businesses"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "businesses:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The business id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BusinessUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "businesses_delete",
        "summary": "Delete a business",
        "description": "Deletes a business that no campaign references. Delete or reassign its campaigns first; there is no cascade.\n\n**Errors**\n\n- `404 NOT_FOUND`: no business with this id in the active Space.\n- `409 CONFLICT`: campaigns still reference the business; `details.campaigns` lists them.",
        "tags": [
          "businesses"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "businesses:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The business id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/assets": {
      "get": {
        "operationId": "assets_list",
        "summary": "List assets",
        "description": "Images, logos and videos registered in the active Space. Filter by `type`.",
        "tags": [
          "assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Only return assets of this type: `IMAGE`, `LOGO` or `VIDEO`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "assets_create",
        "summary": "Create an asset",
        "description": "Registers an image or video by public URL. The asset is stored as a pointer to that URL and is `READY` immediately; the bytes are fetched when a sync needs them. Attach it to a campaign, ad group or ad with the binding operations under *Campaign assets* and *Ad assets*.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssetCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/assets/{id}": {
      "get": {
        "operationId": "assets_get",
        "summary": "Get an asset",
        "description": "Returns one asset.\n\n**Errors**\n\n- `404 NOT_FOUND`: no asset with this id in the active Space.",
        "tags": [
          "assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The asset id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "assets_update",
        "summary": "Update an asset",
        "description": "Partial update of the descriptive fields: `name`, `altText`, `tags`, `metadata`, `status`, `width` and `height`. The URL and content type of an asset cannot change; create a new asset instead.\n\n**Errors**\n\n- `404 NOT_FOUND`: no asset with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The asset id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssetUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "assets_delete",
        "summary": "Delete an asset",
        "description": "Deletes the asset record. The file at the original URL is untouched.\n\n**Errors**\n\n- `404 NOT_FOUND`: no asset with this id in the active Space.",
        "tags": [
          "assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The asset id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/campaign-assets": {
      "get": {
        "operationId": "campaignAssets_list",
        "summary": "List campaign asset bindings",
        "description": "Which assets are attached to which campaigns, and in what role. Filter by `campaignId` or `assetId`.",
        "tags": [
          "campaign-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "campaignId",
            "in": "query",
            "required": false,
            "description": "Only return bindings for this campaign.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assetId",
            "in": "query",
            "required": false,
            "description": "Only return bindings for this asset.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "campaignAssets_create",
        "summary": "Attach an asset to a campaign",
        "description": "Attaches an asset to a campaign in a role such as `MARKETING_IMAGE` or `LOGO`. *Get the media requirements matrix* lists which roles each campaign type needs. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "campaign-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignAssetCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/campaign-assets/{id}": {
      "get": {
        "operationId": "campaignAssets_get",
        "summary": "Get a campaign asset binding",
        "description": "Returns one binding.\n\n**Errors**\n\n- `404 NOT_FOUND`: no binding with this id in the active Space.",
        "tags": [
          "campaign-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The binding id (not the asset or campaign id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "delete": {
        "operationId": "campaignAssets_delete",
        "summary": "Detach an asset from a campaign",
        "description": "Removes the binding. The asset itself is kept. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no binding with this id in the active Space.",
        "tags": [
          "campaign-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "assets:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The binding id (not the asset or campaign id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/ad-assets": {
      "get": {
        "operationId": "adAssets_list",
        "summary": "List ad asset bindings",
        "description": "Which assets are attached to which ads or ad groups, and in what role. These are the images and videos that serve on display, video and Performance Max formats. Filter by `adId`, `adGroupId`, `campaignId` or `role`.",
        "tags": [
          "ad-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "adId",
            "in": "query",
            "required": false,
            "description": "Only return bindings for this ad.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "adGroupId",
            "in": "query",
            "required": false,
            "description": "Only return bindings for this ad group.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaignId",
            "in": "query",
            "required": false,
            "description": "Return every ad and ad-group binding across this campaign.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "role",
            "in": "query",
            "required": false,
            "description": "Only return bindings in this role, for example `MARKETING_IMAGE`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "adAssets_create",
        "summary": "Attach an asset to an ad",
        "description": "Attaches an asset to an ad (`scope: AD`) or to an ad group (`scope: AD_GROUP`) in a role such as `MARKETING_IMAGE` or `VIDEO`. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "ad-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdAssetCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/ad-assets/{id}": {
      "get": {
        "operationId": "adAssets_get",
        "summary": "Get an ad asset binding",
        "description": "Returns one binding.\n\n**Errors**\n\n- `404 NOT_FOUND`: no binding with this id in the active Space.",
        "tags": [
          "ad-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The binding id (not the asset or ad id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "delete": {
        "operationId": "adAssets_delete",
        "summary": "Detach an asset from an ad",
        "description": "Removes the binding. The asset itself is kept. Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no binding with this id in the active Space.",
        "tags": [
          "ad-assets"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The binding id (not the asset or ad id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/creative-vocabulary": {
      "get": {
        "operationId": "creativeVocabulary_get",
        "summary": "Get the creative vocabulary",
        "description": "The fixed registry that angles, concepts and businesses are described with: `angleTypes`, `conceptAttributes` and `businessCategories`, plus a `schemaVersion`. Ids from here are what `angleType`, `attributes` and `categoryId` accept. Entries are only ever added, never renamed or removed, so a cached copy stays valid. Needs no scope.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": []
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/angles": {
      "get": {
        "operationId": "angles_list",
        "summary": "List angles",
        "description": "An angle is the claim a set of ads argues, for example \"cheaper than the incumbent\". Results are measured per angle, so it is the unit you decide at. Filter by `businessId` or `angleType`.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "businessId",
            "in": "query",
            "required": false,
            "description": "Only return angles for this business.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "angleType",
            "in": "query",
            "required": false,
            "description": "Only return angles of this type (an id from the creative vocabulary).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "angles_create",
        "summary": "Create an angle",
        "description": "Creates an angle for a business. `angleType` must be an id from *Get the creative vocabulary*; `name` is your own wording of the claim. The same claim for two businesses is two angles.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AngleCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/angles/{id}": {
      "get": {
        "operationId": "angles_get",
        "summary": "Get an angle",
        "description": "Returns one angle.\n\n**Errors**\n\n- `404 NOT_FOUND`: no angle with this id in the active Space.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The angle id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "patch": {
        "operationId": "angles_update",
        "summary": "Update an angle",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included). `angleType` must remain a creative-vocabulary id.\n\n**Errors**\n\n- `404 NOT_FOUND`: no angle with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The angle id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AngleUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "angles_delete",
        "summary": "Delete an angle",
        "description": "Deletes the angle and its concepts. Ads that referenced it keep their `angleId` stamp, so historical results stay attributable.\n\n**Errors**\n\n- `404 NOT_FOUND`: no angle with this id in the active Space.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The angle id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/concepts": {
      "get": {
        "operationId": "concepts_list",
        "summary": "List concepts",
        "description": "A concept is one way of making its angle's claim; ads reference it through `conceptId`. Filter by `angleId`.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "angleId",
            "in": "query",
            "required": false,
            "description": "Only return concepts of this angle.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "concepts_create",
        "summary": "Create a concept",
        "description": "Creates a concept under an angle. `attributes` are ids from *Get the creative vocabulary*.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConceptCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/concepts/{id}": {
      "get": {
        "operationId": "concepts_get",
        "summary": "Get a concept",
        "description": "Returns one concept.\n\n**Errors**\n\n- `404 NOT_FOUND`: no concept with this id in the active Space.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The concept id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "patch": {
        "operationId": "concepts_update",
        "summary": "Update a concept",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included).\n\n**Errors**\n\n- `404 NOT_FOUND`: no concept with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The concept id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConceptUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "concepts_delete",
        "summary": "Delete a concept",
        "description": "Deletes the concept. Ads that referenced it keep their `conceptId` stamp.\n\n**Errors**\n\n- `404 NOT_FOUND`: no concept with this id in the active Space.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "ads:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The concept id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/angles/readout": {
      "get": {
        "operationId": "angles_readout",
        "summary": "Read out angle results",
        "description": "For each business, where every angle stands. Per angle: a decision `state` (`collecting`, `ahead`, `decided`, `no_difference`), the probability it is the best, the expected monthly loss of keeping it, delivery flags (`skewed`, `starved`, `mixDivergent`), the platform mix, when the state last changed, and how the estimate was produced.\n\nUse it to decide which angles to keep spending on. Narrow with `window`, `businessId` or `platformId`.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "reports:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "window",
            "in": "query",
            "required": false,
            "description": "Look-back window: `30d`, `60d` or `90d`.",
            "schema": {
              "type": "string",
              "default": "90d"
            }
          },
          {
            "name": "businessId",
            "in": "query",
            "required": false,
            "description": "Only this business.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platformId",
            "in": "query",
            "required": false,
            "description": "Only this platform.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/angles/preflight": {
      "get": {
        "operationId": "angles_preflight",
        "summary": "Estimate time to a verdict",
        "description": "Before spending, estimates how many weeks it would take to tell `count` angles apart, using this account's own observed spend, cost per click and conversion rate. Returns a range (the 20th to 80th percentile of simulated outcomes), never a single number.\n\nWhen no plan can resolve in a reasonable time the response says so and offers advice: judge on clicks instead of conversions, or, where conversion tracking is missing, set it up first.",
        "tags": [
          "angles"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "reports:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "count",
            "in": "query",
            "required": true,
            "description": "How many angles you want to compare (2 to 12).",
            "schema": {
              "type": "integer",
              "minimum": 2,
              "maximum": 12
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/media/matrix": {
      "get": {
        "operationId": "media_matrix",
        "summary": "Get the media requirements matrix",
        "description": "For each platform and campaign type: which asset roles are required, which are optional, whether they attach at campaign, ad group or ad level, and the ad format they serve in. Carries a `version` so a client can tell when the requirements changed. Needs no scope.",
        "tags": [
          "media"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": []
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/reports/stats": {
      "get": {
        "operationId": "reports_stats",
        "summary": "Get account-wide stats",
        "description": "Totals for the whole account over a window: impressions, clicks, spend, conversions, conversion value, return on ad spend, click-through rate, cost per click and cost per conversion, plus campaign counts and the total budget. Conversions you uploaded and conversions the platform reported are counted once, never summed twice.\n\nThere is no campaign filter here. For one campaign, ad group, keyword or ad use *Get a performance time series* or *Get a performance breakdown* with `scope=campaign:{id}`.\n\n**Errors**\n\n- `400 UNKNOWN_PARAM`: a query parameter this operation does not know. Unknown parameters are refused rather than silently ignored.\n- `400 UNKNOWN_ACCOUNT`: `accountId` does not match a connected account.",
        "tags": [
          "reports"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "reports:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "platformId",
            "in": "query",
            "required": false,
            "description": "`all`, or one platform id.",
            "schema": {
              "type": "string",
              "default": "all"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "required": false,
            "description": "`all`, or one connected ad account id.",
            "schema": {
              "type": "string",
              "default": "all"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Relative window, for example `7d` or `30d`.",
            "schema": {
              "type": "string",
              "default": "7d"
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "description": "Custom window start (`YYYY-MM-DD`). Use with `endDate`; overrides `period`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "description": "Custom window end (`YYYY-MM-DD`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/reports/disapprovals": {
      "get": {
        "operationId": "reports_disapprovals",
        "summary": "List disapproved ads and keywords",
        "description": "Every ad and keyword that a connected platform currently refuses to show, shows only in a limited way, or shows only to people interested in an area. Each row names the entity, the platform, the reasons and the affected markets. An empty list means nothing is disapproved.\n\n**Errors**\n\n- `400 INVALID_PLATFORM`: `platform` is not a known platform id.",
        "tags": [
          "reports"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "reports:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "Only this platform. Omit for all connected platforms.",
            "schema": {
              "type": "string",
              "enum": [
                "google-ads",
                "microsoft-ads",
                "reddit-ads",
                "meta-ads",
                "openai-ads",
                "linkedin-ads"
              ]
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/reports/timeseries": {
      "get": {
        "operationId": "reports_timeseries",
        "summary": "Get a performance time series",
        "description": "Daily rows (impressions, clicks, spend, conversions, conversion value and the derived rates) for the requested level and scope.\n\n- `compare=previous_period` adds an aligned prior series and per-metric deltas.\n- `groupBy=platform` returns one series per connected platform; `groupBy=entity` one per child entity (for example each ad group of a campaign).\n- Daily data is kept for 90 days. A wider window returns the covered range plus a `coverage` object; it is never silently truncated.",
        "tags": [
          "reports"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "reports:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "level",
            "in": "query",
            "required": false,
            "description": "Which entities the rows describe.",
            "schema": {
              "type": "string",
              "enum": [
                "campaign",
                "adgroup",
                "keyword",
                "ad"
              ],
              "default": "campaign"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "description": "Restrict to a parent, for example `campaign:{id}` or `adGroup:{id}`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "metrics",
            "in": "query",
            "required": false,
            "description": "Comma-separated metric keys to include. Omit for all.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Window start (`YYYY-MM-DD`). With `to`, overrides `period`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Window end (`YYYY-MM-DD`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Relative window when `from` and `to` are omitted: `7d`, `30d` or `90d`.",
            "schema": {
              "type": "string",
              "default": "30d"
            }
          },
          {
            "name": "compare",
            "in": "query",
            "required": false,
            "description": "Set to `previous_period` for an aligned prior series and deltas.",
            "schema": {
              "type": "string",
              "enum": [
                "previous_period"
              ]
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "`all`, or one platform id.",
            "schema": {
              "type": "string",
              "default": "all"
            }
          },
          {
            "name": "groupBy",
            "in": "query",
            "required": false,
            "description": "How to split the series.",
            "schema": {
              "type": "string",
              "enum": [
                "none",
                "entity",
                "platform"
              ],
              "default": "none"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/reports/breakdown": {
      "get": {
        "operationId": "reports_breakdown",
        "summary": "Get a performance breakdown",
        "description": "One row per entity at the requested level: totals for the window, deltas against the previous period, a seven-point sparkline and the parent it belongs to. Sort by any metric and cap the row count with `limit`.",
        "tags": [
          "reports"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "reports:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "level",
            "in": "query",
            "required": false,
            "description": "Which entities the rows describe.",
            "schema": {
              "type": "string",
              "enum": [
                "campaign",
                "adgroup",
                "keyword",
                "ad"
              ],
              "default": "campaign"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "description": "Restrict to a parent, for example `campaign:{id}`; only its children are returned.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Window start (`YYYY-MM-DD`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Window end (`YYYY-MM-DD`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Relative window when `from` and `to` are omitted.",
            "schema": {
              "type": "string",
              "default": "30d"
            }
          },
          {
            "name": "compare",
            "in": "query",
            "required": false,
            "description": "`previous_period` for deltas, or `none`.",
            "schema": {
              "type": "string",
              "default": "previous_period"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "`all`, or one platform id.",
            "schema": {
              "type": "string",
              "default": "all"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Metric key to sort by, highest first.",
            "schema": {
              "type": "string",
              "default": "spend"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows to return (up to 500).",
            "schema": {
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/conversions": {
      "post": {
        "operationId": "conversions_upload",
        "summary": "Upload offline conversions",
        "description": "Reports conversions that happened off your website, such as a booked call or a signed contract, to the ad platform that produced the click, so the platform can credit the ad and learn from it. Send one conversion or a batch of up to 200.\n\nEach conversion needs exactly one click id: `gclid`, `gbraid` or `wbraid` route to Google Ads, `msclkid` to Microsoft Advertising. `orderId` makes the upload idempotent: sending the same `orderId` twice records one conversion.\n\nAlso accepted with the `campaigns:write` scope.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionUpload"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversions/status": {
      "get": {
        "operationId": "conversions_status",
        "summary": "Get offline conversion status",
        "description": "How offline uploads are going in the active Space: the count, the time of the last upload, the total value and the most recent uploads. Narrow to one campaign's conversion actions with `campaignId`, or to specific actions with `actionIds`. Also accepted with the `reports:read` scope.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "query",
            "required": false,
            "description": "Only the conversion actions this campaign bids on.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "actionIds",
            "in": "query",
            "required": false,
            "description": "Comma-separated conversion action ids.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/identify": {
      "post": {
        "operationId": "identify_upsert",
        "summary": "Identify a visitor",
        "description": "Records who a visitor is (by email, phone or your own id, all hashed at rest) together with the ad click id that brought them, so that a conversion reported later by email alone can still be credited to the click. Where the click id is gone, the identity itself is used for Enhanced Conversions for Leads.\n\nAlso accepted with the `campaigns:write` scope, and with a **website key** (`grow_pub_…`), which is the credential the site tag carries. A personal token still works for server-to-server callers.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          },
          {
            "PublishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IdentifyRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversions/events": {
      "post": {
        "operationId": "conversions_events",
        "summary": "Report conversion events",
        "description": "The general way to tell Growomat that something happened: a sign-up, a trial, a qualified lead, a payment or an upgrade. Send events from your own stack (one, an array, or `{ events }`), or pass a provider payload with `source` set to `stripe`, `hubspot`, `salesforce`, `ga4` or `webhook` and let the server normalise it.\n\nA funnel `event` name creates its conversion action on first use; adjustment rows are routed to the adjustments path. Also accepted with the `campaigns:write` scope.\n\nWith a **website key** (`grow_pub_…`) the operation runs in a constrained form: the source is set by the server, `adjustments` are refused (`ADJUSTMENTS_NOT_ALLOWED`), a declared `source` is refused (`SOURCE_NOT_ALLOWED`), conversion actions are never created, and at most 10 events are accepted per request. Sales reported this way are stored as unverified claims and are neither uploaded to an ad platform nor counted in any report until a server-side source confirms the same `orderId`.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          },
          {
            "PublishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionEventsRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversions/adjustments": {
      "post": {
        "operationId": "conversions_adjustments",
        "summary": "Upload conversion adjustments",
        "description": "Corrects conversions you already reported: `RETRACTION` for a refund or churn, `RESTATEMENT` for a new value, `ENHANCEMENT` to add identity data. Each adjustment names the original conversion by `orderId` (or by `gclid` plus `originalConversionDateTime`). Send one or a batch of up to 500. Also accepted with the `campaigns:write` scope.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionAdjustmentsRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversions/health": {
      "get": {
        "operationId": "conversions_health",
        "summary": "Get conversion tracking health",
        "description": "Is the conversion loop working? Per source and overall: the match rate, uploads accepted versus partially failed, adjustments (`adjustments.total`, `applied`, `failed`), the age of the last event, and a plain-language trust line.\n\n`issues[]` carries codes you can act on: `NO_SOURCE_CONNECTED`, `LOW_MATCH_RATE`, `CLICK_ID_LOOP_BROKEN`, `ADJUSTMENTS_FAILING`, `CONSENT_MISSING_EEA`, `ACTION_NOT_PRIMARY`, `DEV_TOKEN_NOT_ENABLED`. Also accepted with the `reports:read` scope.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/conversion-sources": {
      "get": {
        "operationId": "conversionSources_list",
        "summary": "List conversion sources",
        "description": "The revenue and conversion sources connected to the active Space.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "conversionSources_create",
        "summary": "Connect a conversion source",
        "description": "Connects a source of conversions (`stripe`, `webhook`, `hubspot`, `salesforce` or `ga4`) to the active Space, so its events can be reported through *Report conversion events*.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionSourceCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversion-actions": {
      "get": {
        "operationId": "conversionActions_list",
        "summary": "List conversion actions",
        "description": "The conversion actions in the active Space: what counts as a conversion, how it is counted and valued. Platform-side state (the remote link, tag snippets, per-platform sync health) is not part of this response.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "conversionActions_create",
        "summary": "Create a conversion action",
        "description": "Creates the action locally. A sync exports it to the platforms, which assign the remote id and the tag snippet; a client cannot set the platform link itself.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionActionCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversion-actions/{id}": {
      "get": {
        "operationId": "conversionActions_get",
        "summary": "Get a conversion action",
        "description": "Returns one conversion action.\n\n**Errors**\n\n- `404 NOT_FOUND`: no conversion action with this id in the active Space.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversion action id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "conversionActions_update",
        "summary": "Update a conversion action",
        "description": "Partial update. Send only the fields you want to change; omitted fields keep their values, and a field you do send replaces the stored value in full (arrays included). Changes reach the ad platforms on the next sync.\n\n**Errors**\n\n- `404 NOT_FOUND`: no conversion action with this id in the active Space.\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversion action id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionActionUpdate"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "conversionActions_delete",
        "summary": "Delete a conversion action",
        "description": "Deletes the action. While a campaign bids on it the request is refused, because removing the action would change how that campaign spends; pass `?force=true` to delete anyway.\n\n**Errors**\n\n- `404 NOT_FOUND`: no conversion action with this id in the active Space.\n- `409 CONFLICT`: campaigns bid on this action; the message names them.",
        "tags": [
          "conversions"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "conversions:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversion action id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "description": "Delete even when campaigns bid on this action.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/budget": {
      "get": {
        "operationId": "budget_get",
        "summary": "Get the budget and brake state",
        "description": "The account budget (amount, period, the derived daily and monthly caps, currency) and the state of its Emergency Brake: whether it is armed, whether it is currently engaged, and the events it has recorded, including exactly which campaigns each one paused.\n\nThe Emergency Brake pauses campaigns automatically when real month-to-date spend passes the budget. It uses the campaign scopes, so any token that can read campaigns can read it.",
        "tags": [
          "budget"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "put": {
        "operationId": "budget_update",
        "summary": "Set the budget",
        "description": "Sets the budget amount, period, currency and alert threshold, and arms or disarms the Emergency Brake. All fields are optional; send at least one.\n\nThe brake is armed by default. While armed, spending past the budget pauses the affected campaigns and notifies the owner. Disarmed, spending past the budget pauses nothing (alerts still fire). Raising `global_budget` re-arms a brake that already fired this month, so the new amount gets the same protection.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "budget"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BudgetUpdateRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/budget/brake": {
      "get": {
        "operationId": "budget_brake_get",
        "summary": "Get the Emergency Brake status",
        "description": "The brake on its own: armed, engaged, its recorded events, and which levels the owner has overruled for the current month. Cheap to poll; no side effects.",
        "tags": [
          "budget"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/budget/brake/release": {
      "post": {
        "operationId": "budget_brake_release",
        "summary": "Release the Emergency Brake",
        "description": "Re-enables exactly the campaigns the brake paused, and only those still paused; a campaign changed since is left alone. **This resumes real ad spend.**\n\n- `mode: \"raise\"` (default) re-arms the brake against the budget's current amount for the rest of the month.\n- `mode: \"resume\"` records that the owner knowingly overruled the budget and leaves that level disarmed until next month.",
        "tags": [
          "budget"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "campaigns:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrakeReleaseRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/sync": {
      "post": {
        "operationId": "sync_trigger",
        "summary": "Trigger a sync",
        "description": "Pushes the active Space's campaigns to the connected ad platforms (Google Ads, Microsoft Advertising, Reddit Ads, Meta Ads, ChatGPT Ads and LinkedIn Ads) and imports changes made on the platforms. This is the only operation that makes campaigns live, and it can start real spend.\n\nThe sync runs in the background: the response arrives immediately with a `syncId` and `status: started`, or `status: nothing_to_sync`. Poll *Get a sync* until it finishes.\n\nRecommended flow: call *Preview a sync plan* first, review it, then pass its `planHash` here so the sync executes exactly what you reviewed. Scope it with `campaignIds`, choose directions per platform with `platformModes`, and use `force` to re-export unchanged campaigns.\n\nThe platform connection belongs to the account; the campaigns synced are the active Space's.\n\n**Errors**\n\n- `409 SYNC_ALREADY_ACTIVE`: a sync is already running in this Space; `details` carries `activeSyncId` and `startedAt`.\n- `409 SYNC_SCOPE_EXCEEDED`: the plan would touch campaigns outside `campaignIds`; set `confirmFullSpace` to allow it.\n- `409 SYNC_PLAN_DRIFT`: the plan changed since the `planHash` you passed was computed; preview again.\n- `400 INVALID_PLATFORM_MODES`: `platformModes` names an unknown platform or direction.",
        "tags": [
          "sync"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "sync:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SyncTriggerRequest"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "sync_list",
        "summary": "List recent syncs",
        "description": "Recent sync records for the active Space, newest first.",
        "tags": [
          "sync"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "sync:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/sync/plan": {
      "get": {
        "operationId": "sync_plan",
        "summary": "Preview a sync plan",
        "description": "Computes what a sync would import, export and remove, per platform, without executing anything. Honours the same `campaign_ids` scope and `force` flag as *Trigger a sync*.\n\nThe response carries a `planHash`. Pass it to *Trigger a sync* to execute exactly this plan; if anything changed in between, the trigger fails with `SYNC_PLAN_DRIFT` instead of running an unreviewed plan.",
        "tags": [
          "sync"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "sync:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "platforms",
            "in": "query",
            "required": false,
            "description": "Comma-separated platform ids, for example `google-ads,microsoft-ads,reddit-ads,meta-ads,openai-ads,linkedin-ads`. Defaults to every connected platform.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaign_ids",
            "in": "query",
            "required": false,
            "description": "Comma-separated campaign ids. Preview only these, as a scoped trigger would.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "description": "Preview re-exporting the in-scope campaigns even when nothing changed.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/sync/{id}": {
      "get": {
        "operationId": "sync_get",
        "summary": "Get a sync",
        "description": "The full record of one sync: status, per-campaign progress, totals and structured errors. Poll it after *Trigger a sync*.\n\nRead `effectiveStatus`: `queued` and `in_progress` mean keep polling; `stalled` means the run has shown no activity for over ten minutes and will be reaped; `completed`, `partial`, `failed` and `cancelled` are final. `partial` means some campaigns failed; their `errorCode` and `errorDetails` are in `progress.campaignDetails[]`.\n\n**Errors**\n\n- `404 NOT_FOUND`: no sync with this id in the active Space.",
        "tags": [
          "sync"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "sync:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The sync id, as returned by *Trigger a sync* (`syncId`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/sync/{id}/cancel": {
      "post": {
        "operationId": "sync_cancel",
        "summary": "Cancel a sync",
        "description": "Asks a running sync to stop. Cancellation takes effect after the campaign currently being processed; that campaign is finished, not left half-written.\n\n**Errors**\n\n- `404 NOT_FOUND`: no sync with this id in the active Space.",
        "tags": [
          "sync"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "sync:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The sync id, as returned by *Trigger a sync* (`syncId`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/sync/unlink": {
      "post": {
        "operationId": "sync_unlink",
        "summary": "Unlink campaigns from a platform",
        "description": "Forgets the link between local campaigns and their copies on one platform, keeping the local campaigns. With `deleteRemote: true` the platform copies are removed as well. The next sync re-creates the campaigns on the platform from scratch, which is the clean way to recover from a campaign that was deleted or broken on the platform side.\n\nThe response maps each campaign id to its result; a campaign that was not linked reports `not_linked`.\n\n**Errors**\n\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "sync"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "sync:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SyncUnlinkRequest"
              }
            }
          }
        }
      }
    },
    "/api/v1/content": {
      "get": {
        "operationId": "content_list",
        "summary": "List content drafts",
        "description": "The social content library of the active Space: every draft with its `phase`. Filter by `businessId`.",
        "tags": [
          "content"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "content:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many items to return per page. Default 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Where to continue from: the `meta.nextCursor` value of the previous page. Omit it for the first page.",
            "schema": {
              "type": [
                "string",
                "number"
              ]
            }
          },
          {
            "name": "businessId",
            "in": "query",
            "required": false,
            "description": "Only return drafts for this business.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      },
      "post": {
        "operationId": "content_create",
        "summary": "Create a content draft",
        "description": "Starts a new piece of social content from a `goal` for a business. The response arrives immediately with the queued draft; the writing continues in the background.\n\nPoll *Get a content draft* and watch `phase`: `queued`, `gathering`, `synthesizing` and `drafting` mean it is still working; `interviewing` and `review` mean it is waiting on you (answer questions, or confirm); `ready` and `failed` are final.\n\nCreating a draft counts against the plan's monthly draft allowance.\n\n**Errors**\n\n- `402 LIMIT_REACHED`: social content is not in the plan, or this month's allowance is spent. The body is a `LimitReachedBody`; read `reason` to tell `NOT_IN_PLAN` from `QUOTA_SPENT`, because only the second one comes back on a date (`resetAt`).\n- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.",
        "tags": [
          "content"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "402": {
            "description": "Social content is not in the plan, or the monthly draft allowance is spent. Read `reason` to tell the two apart.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitReachedBody"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "content:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContentDraftCreate"
              }
            }
          }
        }
      }
    },
    "/api/v1/content/{id}": {
      "get": {
        "operationId": "content_get",
        "summary": "Get a content draft",
        "description": "The draft with its `phase`, the piece written so far, and any open interview questions (`openQuestions[]`) waiting for an answer.\n\n**Errors**\n\n- `404 NOT_FOUND`: no draft with this id in the active Space.",
        "tags": [
          "content"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "content:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The content draft id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/content/{id}/export": {
      "get": {
        "operationId": "content_export",
        "summary": "Export a finished piece",
        "description": "Returns the piece as `{ markdown, title, platform, sources, wordCount }`.\n\n**Errors**\n\n- `404 NOT_FOUND`: no draft with this id in the active Space.\n- `409 QUESTIONS_OPEN`: a required interview question is still unanswered. Answer it first; the same rule gates *Confirm a content draft*.",
        "tags": [
          "content"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "content:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The content draft id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/content/{id}/answer": {
      "post": {
        "operationId": "content_answer",
        "summary": "Answer an interview question",
        "description": "Answers one of the draft's `openQuestions[]`. The value type follows the question's `kind`: a boolean, a single choice, or free text.\n\n**Errors**\n\n- `404 NOT_FOUND`: no draft with this id in the active Space.\n- `400 VALIDATION_ERROR`: unknown `questionId` or an answer of the wrong type.",
        "tags": [
          "content"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "content:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The content draft id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContentDraftAnswer"
              }
            }
          }
        }
      }
    },
    "/api/v1/content/{id}/retry": {
      "post": {
        "operationId": "content_retry",
        "summary": "Retry a content draft",
        "description": "Restarts a draft that failed or got stuck. From `review` it rewrites the piece with your feedback applied.\n\n**Errors**\n\n- `404 NOT_FOUND`: no draft with this id in the active Space.\n- `409 ALREADY_RUNNING`: the draft is still being written.\n- `409 ALREADY_READY`: the piece has already been confirmed.",
        "tags": [
          "content"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "content:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The content draft id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ]
      }
    },
    "/api/v1/content/{id}/confirm": {
      "post": {
        "operationId": "content_confirm",
        "summary": "Confirm a content draft",
        "description": "Moves a reviewed piece from `review` to `ready`. When interview answers changed after the piece was written, confirming rewrites it first: the response is then `202` with a draft that lands in `ready` on its own, and the rewrite counts as one draft against the allowance. Pass `skipRewrite: true` to finish without the rewrite.\n\n**Errors**\n\n- `404 NOT_FOUND`: no draft with this id in the active Space.\n- `409 QUESTIONS_OPEN`: a required interview question is still unanswered.\n- `409 BRAND_VIOLATION`: the piece uses a phrase the business's brand profile bans.",
        "tags": [
          "content"
        ],
        "responses": {
          "200": {
            "description": "OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Created. The new resource is in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "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`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiToken": [
              "content:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The content draft id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Space-Id",
            "in": "header",
            "required": false,
            "description": "The Space to work in, for example `spc_8f3aK2`. Omit it to use your personal Space. *List your Spaces* returns the ids. A Space that does not exist returns `404 SPACE_NOT_FOUND`; one you are not a member of returns `403 NOT_A_MEMBER`.",
            "schema": {
              "type": "string",
              "pattern": "^spc_[A-Za-z0-9]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContentDraftConfirm"
              }
            }
          }
        }
      }
    }
  },
  "x-scopes": [
    "campaigns:read",
    "campaigns:write",
    "conversions:read",
    "conversions:write",
    "ads:read",
    "ads:write",
    "keywords:read",
    "keywords:write",
    "reports:read",
    "businesses:read",
    "businesses:write",
    "assets:read",
    "assets:write",
    "sync:read",
    "sync:write",
    "content:read",
    "content:write"
  ]
}