{
  "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", "enum": ["operational", "degraded"], "description": "Whether delivery reporting is healthy for this account. degraded means the daily rollups have fallen behind, so /stats reads are falling through to live ledger scans and large or unaligned windows may be slow or refused. Reporting still answers; prefer last_days and narrower windows until it clears."},
              "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"},
          "served_from": {"type": "string", "enum": ["daily_rollup", "ledger"], "description": "Which store answered. daily_rollup is pre-aggregated closed UTC days and is the fast, flat-cost path; ledger is a live scan whose cost grows with window length times delivery volume. Whole-UTC-day bounds at day or month granularity are rollup-served; hour granularity, a partial trailing day, and any country filter always need the ledger."},
          "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", "description": "Inclusive start of the window (UTC). Optional. When BOTH start and end are omitted at day granularity the window is the last seven complete UTC days. The end is normally today's midnight, but may be a day earlier while that day is still being rolled up, so read the echoed start/end rather than assuming them. Explicit bounds are honoured verbatim and never rounded.", "schema": {"type": "string", "format": "date-time"}},
          {"name": "end", "in": "query", "description": "Exclusive end of the window (UTC). Optional; see start. A partial trailing day forces a live ledger scan and is materially slower than whole-UTC-day bounds on high-volume accounts.", "schema": {"type": "string", "format": "date-time"}},
          {"name": "last_days", "in": "query", "description": "Relative window: the last N COMPLETE UTC days. Preferred over start/end -- the server computes the bounds, so they are whole-day aligned by construction and served from the daily rollups. last_days=7 is identical to sending no window at all. Cannot be combined with start or end (400 conflicting_window), and still obeys the per-granularity range ceiling.", "schema": {"type": "integer", "minimum": 1, "maximum": 3653}},
          {"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"
          }
        }
      }
    }
  }
}
