{
  "openapi": "3.1.0",
  "info": {
    "title": "Kickbacks Advertiser API",
    "version": "1.7.0",
    "description": "Authenticated reporting is available to every advertiser. Campaign creation and mutation additionally require manual write approval, accepted API terms, and an operational billing arrangement. Advertisers can choose weekly saved-card auto-pay or request separately approved Net 30; both collect authoritative delivered value without campaign prepayment. Reveal your pre-generated API key and manage billing at https://kickbacks.ai/advertise.",
    "contact": {
      "email": "support@kickbacks.ai",
      "url": "https://kickbacks.ai/advertise"
    }
  },
  "servers": [
    {
      "url": "https://kickbacks.ai/api/advertiser/v1"
    }
  ],
  "security": [
    {
      "userId": [],
      "apiKey": []
    }
  ],
  "components": {
    "securitySchemes": {
      "userId": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Kickbacks-User-Id",
        "description": "Your account user id, exactly as shown in the portal API-key panel and in POST /advertise key responses."
      },
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Kickbacks-Api-Key",
        "description": "Your kb_live_\u2026 API key. It is pre-generated and hidden by default; the signed-in account owner can reveal or rotate it in the portal. Both headers are required on every request."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Every non-2xx response carries a `detail` \u2014 either a string or a {field, reason} object naming the offending input.",
        "properties": {
          "detail": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "properties": {
                  "field": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            ]
          }
        }
      },
      "CampaignCreate": {
        "type": "object",
        "required": [
          "ad_line",
          "destination_url",
          "cpm_usd",
          "impressions",
          "idempotency_key"
        ],
        "properties": {
          "ad_line": {
            "type": "string",
            "minLength": 3,
            "maxLength": 60,
            "description": "The ad creative: a short 'thinking\u2026' verb line rendered in developers' coding tools. 3\u201360 chars (~30\u201340 renders best); no control characters; emoji and symbols allowed."
          },
          "destination_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Click-through target. https:// only, no embedded credentials, max 500 chars."
          },
          "brand": {
            "type": "string",
            "maxLength": 120,
            "description": "Optional brand name, shown on the public leaderboard when optin_leaderboard is true."
          },
          "name": {
            "type": "string",
            "maxLength": 120,
            "description": "Campaign display name in your dashboard. Defaults to 'API campaign \u2014 <date>'."
          },
          "cpm_usd": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Your bid: price per 1,000 impressions in USD. Must meet the current floor (GET /account \u2192 config.min_cpm_usd, currently $0.50). Higher bids serve sooner \u2014 the live book is price-ranked."
          },
          "impressions": {
            "type": "integer",
            "minimum": 1000,
            "maximum": 100000,
            "multipleOf": 1000,
            "description": "Total impressions to purchase. Sold in blocks of 1,000; each block is ten-second viewable impressions."
          },
          "kind": {
            "type": "string",
            "enum": [
              "impression",
              "click"
            ],
            "default": "impression"
          },
          "target_surface": {
            "type": "string",
            "enum": [
              "extension",
              "terminal"
            ],
            "description": "Only when config.surface_split_enabled is true: run on the in-editor surface or the CLI status line. Sending it while the split is disabled is a 400 (never silently ignored)."
          },
          "target_countries": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[A-Z]{2}$"
            },
            "maxItems": 20,
            "description": "Only when config.country_targeting_enabled is true: ISO-3166 alpha-2 allow-list. Omit for global delivery."
          },
          "pace_minutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10080,
            "description": "Optional delivery pacing: target minutes to spread the full purchase over (max 1 week). Omit to deliver as fast as the auction allows."
          },
          "icon_url": {
            "type": "string",
            "maxLength": 12000,
            "description": "Optional icon: an https URL or data: URI; stored and served alongside the creative."
          },
          "optin_leaderboard": {
            "type": "boolean",
            "default": true
          },
          "start_paused": {
            "type": "boolean",
            "default": false,
            "description": "Stage the campaign behind the authoritative serving kill switch. The campaign and its blocks are created and billed normally, but no delivery begins until POST /campaigns/{campaign_id}/resume."
          },
          "idempotency_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9_.:\\-]{1,64}$",
            "description": "REQUIRED. Your unique reference for this order. Retrying with the same key returns the same campaign instead of double-booking; reusing a key with a different payload is a 409."
          }
        }
      },
      "CampaignCreated": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string"
          },
          "creation_channel": {
            "type": "string",
            "const": "advertiser_api",
            "description": "Strongly typed campaign entry channel. API-created campaigns always return advertiser_api."
          },
          "status": {
            "type": "string",
            "enum": ["active", "paused"]
          },
          "moderation_status": {
            "type": "string",
            "enum": [
              "approved",
              "pending"
            ]
          },
          "impressions": {
            "type": "integer"
          },
          "blocks": {
            "type": "integer"
          },
          "cpm_usd": {
            "type": "number"
          },
          "amount_cents": {
            "type": "integer",
            "description": "Maximum order face-value estimate in cents USD; invoices collect authoritative delivered value only."
          },
          "currency": {
            "type": "string"
          },
          "billing": {
            "type": "string",
            "const": "delivered_value_postpay"
          },
          "replayed": {
            "type": "boolean",
            "description": "True when this response is an idempotent replay of an earlier create."
          }
        }
      },
      "Campaign": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string"
          },
          "creation_channel": {
            "type": "string",
            "enum": [
              "advertiser_api",
              "website",
              "admin",
              "legacy"
            ],
            "description": "Strongly typed campaign entry channel. Campaigns created through this API are advertiser_api; legacy means the campaign predates this stamp."
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "active | paused | cancelled_user | \u2026 (delivery state lives on blocks)"
          },
          "created_at": {
            "type": "string"
          },
          "ad_line": {
            "type": [
              "string",
              "null"
            ]
          },
          "destination_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "moderation_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "lifetime_spent_micros": {
            "type": "integer",
            "description": "Delivered value to date in micro-USD."
          },
          "report_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "When present, a shareable delivery report exists at https://kickbacks.ai/r/{report_uuid}."
          },
          "blocks": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Per-block delivery state (status, impressions_served, impressions_target\u2026)."
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string"
          },
          "key_prefix": {
            "type": "string"
          },
          "key_last_used_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "outstanding_cents": {
            "type": "integer",
            "description": "Your not-yet-invoiced balance used by the ordering spend cap. See balance.owed_cents for the complete payable balance."
          },
          "currency": {
            "type": "string"
          },
          "billing": {
            "type": "string"
          },
          "billing_email": {
            "type": ["string", "null"]
          },
          "balance": {
            "$ref": "#/components/schemas/Balance"
          },
          "billing_profile": {
            "$ref": "#/components/schemas/BillingProfile"
          },
          "access": {
            "type": "object",
            "description": "Reporting is available to every authenticated advertiser. Campaign writes require manual approval and accepted API terms.",
            "properties": {
              "reporting": {"type": "string", "const": "operational"},
              "campaign_writes": {"type": "string", "enum": ["operational", "pending_approval", "terms_required", "billing_required", "disabled"]},
              "write_approved": {"type": "boolean"},
              "terms_accepted": {"type": "boolean"}
            }
          },
          "config": {
            "type": "object",
            "description": "Live order gates \u2014 read these instead of hard-coding limits.",
            "properties": {
              "block_impressions": {
                "type": "integer"
              },
              "min_cpm_usd": {
                "type": "number"
              },
              "max_total_usd": {
                "type": "number"
              },
              "max_blocks": {
                "type": "integer"
              },
              "surface_split_enabled": {
                "type": "boolean"
              },
              "country_targeting_enabled": {
                "type": "boolean"
              },
              "outstanding_balance_cap_usd": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "create_rate_limit_per_min": {
                "type": "integer"
              },
              "read_rate_limit_per_min": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Balance": {
        "type": "object",
        "description": "Delivered-value automatic-collection balance in cents USD.",
        "properties": {
          "owed_cents": {"type": "integer"},
          "pending_cents": {"type": "integer"},
          "invoiced_cents": {"type": "integer"},
          "paid_cents": {"type": "integer"},
          "voided_cents": {"type": "integer"},
          "credits_cents": {"type": "integer"},
          "pending_charge_count": {"type": "integer"},
          "invoiced_charge_count": {"type": "integer"},
          "last_invoiced_at": {"type": ["string", "null"], "format": "date-time"},
          "next_invoice_at": {"type": ["string", "null"], "format": "date-time"},
          "unbilled_delivered_micros": {"type": "integer"},
          "collection_status": {"type": "string"},
          "amount_basis": {"type": "string", "const": "delivered_value"},
          "cadence": {"type": "string", "enum": ["weekly_monday_0600_utc", "monthly_1st_0600_utc_net30", "semi_monthly_1_15"]},
          "billing_mode": {"type": "string", "enum": ["weekly_autopay", "net30_invoice", "legacy_semimonthly_autopay"]}
        }
      },
      "BillingProfile": {
        "type": "object",
        "description": "Stripe-hosted post-pay readiness. No full payment credentials are returned.",
        "properties": {
          "collection_status": {"type": "string", "enum": ["not_enrolled", "setup_pending", "ready", "payment_failed", "action_required", "paused", "disabled"]},
          "requested_billing_mode": {"type": ["string", "null"], "enum": ["weekly_autopay", "net30_invoice", null]},
          "effective_billing_mode": {"type": "string", "enum": ["disabled", "weekly_autopay", "net30_invoice"]},
          "credit_limit_cents": {"type": "integer", "minimum": 0},
          "tax_ready": {"type": "boolean"},
          "next_bill_at": {"type": ["string", "null"], "format": "date-time"},
          "payment_method": {
            "type": ["object", "null"],
            "properties": {"brand": {"type": ["string", "null"]}, "last4": {"type": ["string", "null"]}}
          }
        }
      },
      "Stats": {
        "type": "object",
        "description": "Stable delivery metrics for the exact inclusive-start, exclusive-end interval. Bucket boundaries are UTC. Account responses omit zero-activity buckets; an empty points array with zero totals is a measured no-activity result, while unavailable reporting is non-2xx. Totals contain exact account-level distinct viewers; points contain distinct viewers within each bucket and must not be summed.",
        "properties": {
          "granularity": {"type": "string", "enum": ["hour", "day", "month"]},
          "start": {"type": "string", "format": "date-time"},
          "end": {"type": "string", "format": "date-time"},
          "filters": {"type": "object"},
          "totals": {"$ref": "#/components/schemas/MetricSet"},
          "points": {
            "type": "array",
            "items": {
              "allOf": [
                {"$ref": "#/components/schemas/MetricSet"},
                {"type": "object", "properties": {"bucket": {"type": "string", "format": "date-time"}}}
              ]
            }
          }
        }
      },
      "MetricSet": {
        "type": "object",
        "properties": {
          "billable_impressions": {"type": "integer"},
          "viewable_ticks": {"type": "integer"},
          "clicks": {"type": "integer"},
          "events": {"type": "integer"},
          "unique_viewers": {"type": "integer"},
          "spend_micros": {"type": "integer", "minimum": 0, "description": "Billable spend in millionths of one US dollar."},
          "ctr": {"type": "number", "description": "clicks / billable_impressions; present on totals"}
        }
      },
      "ExportCreate": {
        "type": "object",
        "required": ["start", "end"],
        "properties": {
          "start": {"type": "string", "format": "date-time"},
          "end": {"type": "string", "format": "date-time"},
          "campaign_ids": {"type": "array", "maxItems": 100, "items": {"type": "string"}},
          "creative_ids": {"type": "array", "maxItems": 100, "items": {"type": "string"}},
          "surfaces": {"type": "array", "maxItems": 20, "items": {"type": "string"}},
          "countries": {"type": "array", "maxItems": 50, "items": {"type": "string"}}
        }
      },
      "ExportJob": {
        "type": "object",
        "properties": {
          "id": {"type": "string"},
          "status": {"type": "string", "enum": ["queued", "running", "ready", "failed"]},
          "schema_version": {"type": "string", "enum": ["ledger-export-v1", "ledger-export-v2"], "description": "The CSV column contract this artifact was generated under, fixed when the job was queued. v2 appends stripe_billing_country; jobs queued under v1 keep reporting v1 until they expire."},
          "filters": {"type": "object"},
          "row_count": {"type": ["integer", "null"]},
          "size_bytes": {"type": ["integer", "null"]},
          "error_code": {"type": ["string", "null"]},
          "created_at": {"type": "string", "format": "date-time"},
          "expires_at": {"type": "string", "format": "date-time"},
          "download_url": {"type": "string", "description": "Present only when status is ready."}
        }
      },
      "CampaignEdit": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "ad_line": {
            "type": "string",
            "minLength": 3,
            "maxLength": 60,
            "description": "Rewrites the live creative in place; re-validated and re-screened."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid credentials (uniform for unknown user id, wrong key, or revoked key).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Unknown campaign id, or a campaign owned by another account (indistinguishable by design).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded; honor Retry-After.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "WriteAccessRequired": {
        "description": "Campaign mutation requires manual write approval and accepted Advertiser API terms. Reporting reads remain available.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
      }
    }
  },
  "paths": {
    "/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Account, balance, and live order config \u2014 call this first",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/stats": {
      "get": {
        "operationId": "getStats",
        "summary": "Account delivery metrics, optionally filtered by campaign, creative, surface, or country",
        "parameters": [
          {"name": "start", "in": "query", "schema": {"type": "string", "format": "date-time"}},
          {"name": "end", "in": "query", "schema": {"type": "string", "format": "date-time"}},
          {"name": "granularity", "in": "query", "schema": {"type": "string", "enum": ["hour", "day", "month"], "default": "day"}},
          {"name": "campaign_id", "in": "query", "description": "Comma-separated campaign IDs.", "schema": {"type": "string"}},
          {"name": "creative_id", "in": "query", "description": "Comma-separated creative IDs.", "schema": {"type": "string"}},
          {"name": "group_by", "in": "query", "description": "Use creative with creative_id to return aligned, zero-filled series per creative.", "schema": {"type": "string", "enum": ["account", "creative"], "default": "account"}},
          {"name": "surface", "in": "query", "description": "Comma-separated surfaces.", "schema": {"type": "string"}},
          {"name": "country", "in": "query", "description": "Comma-separated ISO country codes.", "schema": {"type": "string"}}
        ],
        "responses": {
          "200": {"description": "OK", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Stats"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    },
    "/exports": {
      "post": {
        "operationId": "createLedgerExport",
        "summary": "Queue a sanitized raw-ledger CSV export",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExportCreate"}}}},
        "responses": {
          "202": {"description": "Export queued", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExportJob"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    },
    "/exports/{export_id}": {
      "parameters": [{"name": "export_id", "in": "path", "required": true, "schema": {"type": "string"}}],
      "get": {
        "operationId": "getLedgerExport",
        "summary": "Get export status",
        "responses": {
          "200": {"description": "Export status", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExportJob"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },
    "/exports/{export_id}/download": {
      "parameters": [{"name": "export_id", "in": "path", "required": true, "schema": {"type": "string"}}],
      "get": {
        "operationId": "downloadLedgerExport",
        "summary": "Download a ready, unexpired CSV export",
        "responses": {
          "200": {"description": "Sanitized CSV", "content": {"text/csv": {"schema": {"type": "string"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "409": {"description": "Export is not ready."},
          "410": {"description": "Export expired."}
        }
      }
    },
    "/campaigns": {
      "post": {
        "operationId": "createCampaign",
        "summary": "Create an active or staged-paused campaign (delivered-value auto-pay)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Campaign created and entered into the live auction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignCreated"
                }
              }
            }
          },
          "400": {
            "description": "Validation failure \u2014 detail.field names the input, detail.reason says why (e.g. min_bid_$0.50, surface_split_not_enabled, content-safety rejection).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Automatic billing setup/payment recovery or the exposure cap requires attention.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {"$ref": "#/components/responses/WriteAccessRequired"},
          "409": {
            "description": "idempotency_key was already used with a different payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "Two cases, distinguished by detail: the API is not accepting orders (do not loop), or 'order provisioning incomplete' \u2014 retry the identical request with the SAME idempotency_key to finish provisioning."
          }
        }
      },
      "get": {
        "operationId": "listCampaigns",
        "summary": "List your campaigns with delivery state",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Campaign"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/campaigns/{campaign_id}": {
      "parameters": [
        {
          "name": "campaign_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getCampaign",
        "summary": "One campaign with delivery state",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "editCampaign",
        "summary": "Rename the campaign and/or rewrite the live ad line",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignEdit"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated."
          },
          "400": {
            "description": "Validation failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {"$ref": "#/components/responses/WriteAccessRequired"},
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "cancelCampaign",
        "summary": "Cancel \u2014 stops serving; delivered value follows the account's active billing mode",
        "responses": {
          "200": {
            "description": "Cancelled."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {"$ref": "#/components/responses/WriteAccessRequired"},
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/status": {
      "get": {
        "security": [],
        "operationId": "getCurrentStatus",
        "summary": "Current passive component status (no historical uptime claim)",
        "responses": {"200": {"description": "Fixed component states: operational, degraded, disabled, or unknown. Component states are system-wide, not per-account: an operational campaign_creation means the platform accepts campaigns, not that the calling account may post one. The write_requirements array lists the per-account prerequisites (write_approval, api_terms, billing_enrollment)."}, "429": {"$ref": "#/components/responses/RateLimited"}}
      }
    },
    "/creatives": {
      "get": {
        "operationId": "listCreatives",
        "summary": "List owned creatives with allocation-safe inventory and typed delivery-metric availability",
        "description": "For collections within the current 500-row bound, presentation, targeting, allocation, campaign/block served counts, remaining inventory, and pacing are returned without a lifetime ledger scan. When contiguous closed-day delivery-rollup coverage exists, delivery.metrics_availability is complete and exact ledger-derived metrics are returned. Otherwise availability is temporarily_unavailable and those fields are null, never measured zero. The single-creative route uses the same coverage gate.",
        "responses": {"200": {"description": "Bounded creative collection with explicit metric availability."}, "401": {"$ref": "#/components/responses/Unauthorized"}, "429": {"$ref": "#/components/responses/RateLimited"}}
      }
    },
    "/creatives/{creative_id}": {
      "parameters": [{"name": "creative_id", "in": "path", "required": true, "schema": {"type": "string"}}],
      "get": {
        "operationId": "getCreative",
        "summary": "Retrieve one owned creative with allocation-safe inventory and typed metric availability",
        "responses": {"200": {"description": "Bounded creative resource. Exact ledger-derived metrics are returned with delivery.metrics_availability=complete when contiguous closed-day rollup coverage exists; otherwise those fields are null with temporarily_unavailable."}, "401": {"$ref": "#/components/responses/Unauthorized"}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/preview": {
      "post": {
        "operationId": "previewCreativePayload",
        "summary": "Validate an unfunded creative and create an expiring, presentation-only preview",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["ad_line", "destination_url"], "properties": {"ad_line": {"type": "string", "minLength": 3, "maxLength": 60}, "destination_url": {"type": "string", "format": "uri"}, "brand": {"type": "string", "maxLength": 120}, "icon_url": {"type": "string", "maxLength": 12000}, "target_surface": {"type": "string", "enum": ["extension", "terminal", "both"]}}}}}},
        "responses": {"201": {"description": "Opaque 24-hour preview capability plus render payload and preview_url."}, "400": {"description": "Creative validation failed."}, "401": {"$ref": "#/components/responses/Unauthorized"}, "429": {"$ref": "#/components/responses/RateLimited"}}
      }
    },
    "/campaigns/{campaign_id}/preview": {
      "parameters": [{"name": "campaign_id", "in": "path", "required": true, "schema": {"type": "string"}}],
      "post": {
        "operationId": "previewCampaign",
        "summary": "Create an expiring presentation snapshot for an owned campaign",
        "responses": {"201": {"description": "Opaque 24-hour preview capability plus preview_url."}, "401": {"$ref": "#/components/responses/Unauthorized"}, "404": {"$ref": "#/components/responses/NotFound"}, "429": {"$ref": "#/components/responses/RateLimited"}}
      }
    },
    "/previews/{preview_token}": {
      "parameters": [{"name": "preview_token", "in": "path", "required": true, "schema": {"type": "string"}}],
      "delete": {
        "operationId": "revokePreview",
        "summary": "Revoke an owned preview capability",
        "responses": {"200": {"description": "Revocation result."}, "401": {"$ref": "#/components/responses/Unauthorized"}}
      }
    },
    "/campaigns/{campaign_id}/pause": {
      "parameters": [
        {
          "name": "campaign_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "pauseCampaign",
        "summary": "Pause serving (keeps book position; resume restores exactly)",
        "responses": {
          "200": {
            "description": "Paused."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {"$ref": "#/components/responses/WriteAccessRequired"},
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Campaign is not in a pausable state (e.g. already cancelled)."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/campaigns/{campaign_id}/resume": {
      "parameters": [
        {
          "name": "campaign_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "resumeCampaign",
        "summary": "Resume a paused campaign",
        "responses": {
          "200": {
            "description": "Resumed."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {"$ref": "#/components/responses/WriteAccessRequired"},
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Campaign is not paused/active."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  }
}
