{
  "openapi": "3.1.0",
  "info": {
    "title": "AppGoat API",
    "version": "1.0.0",
    "summary": "App Store + Google Play data for agents and apps.",
    "description": "Every app and developer in both stores, daily top charts, reviews, change feeds, metric history and keyword\n(search-rank) intelligence.\n\n**Conventions**\n- `{app}` accepts anything that identifies an app: an App Store id (`284882215`, `id284882215`), a Play\n  package (`com.spotify.music`), an Apple bundle id (`com.spotify.client`), a store URL, `apple:<id>` /\n  `play:<id>`, or a name (`spotify` → best match, with a warning in `meta.warnings`). Add `store=apple|play`\n  to disambiguate. `data.linked` is the same app in the other store when we know it.\n- Freshness is automatic: app-scoped reads refresh data older than `max_age` seconds from the store first\n  (1 credit per country fetched, capped at 10). `wait=true` (default) blocks up to ~25 s; on timeout, or with\n  `wait=false`, you get the cached data with `meta.freshness.refreshing: true` and a `job_id` to poll at\n  `GET /v1/jobs/{id}` (free).\n- `country` defaults to `us`; pass a comma list (paid plans) or `all` (Studio).\n- `include=` adds sections, `fields=` keeps only the listed top-level fields, lists page with `cursor`\n  (pass `meta.next_cursor` back unchanged).\n- Every success is `{ data, meta }`; `meta.credits_used` is what the call cost and `meta.credits_remaining`\n  what is left. Errors are `{ error: { code, message, hint, docs } }`: read `hint`, it says what to do next.\n- `x-credits` on each operation documents its price.\n- Watches turn store changes into events: `POST /v1/watches`, then poll `GET /v1/events` or add a webhook\n  (`POST /v1/webhooks`). Watches, events and webhooks are free; only the plan's watched-app and keyword-slot\n  limits apply. `/v1/tracked-keywords` is deprecated: use keyword watches.\n",
    "contact": {
      "email": "support@appgoat.ai",
      "url": "https://appgoat.ai"
    }
  },
  "servers": [
    {
      "url": "https://api.appgoat.ai/v1"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Apps"
    },
    {
      "name": "Search & charts"
    },
    {
      "name": "Keywords"
    },
    {
      "name": "Developers"
    },
    {
      "name": "Audits"
    },
    {
      "name": "Watches & events",
      "description": "Watch apps, competitors and keywords; significant changes become events (feed, webhooks, Slack, GitHub, daily email digest)."
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Connections"
    },
    {
      "name": "Actions"
    },
    {
      "name": "Jobs"
    },
    {
      "name": "Account"
    }
  ],
  "paths": {
    "/apps/{app}": {
      "get": {
        "operationId": "getApp",
        "tags": [
          "Apps"
        ],
        "summary": "Get everything about one app (metadata, ratings, price, markets, linked app in the other store).",
        "description": "Use this first when a user mentions an app. Pass any identifier or a name in `{app}`. Data older than\n`max_age` (default 7 days) is refreshed from the store; an app we never saw is fetched on the spot (if it\nis still being fetched after ~25 s you get HTTP 202 with `data: null` and a `job_id`).\n`include` adds sections: `markets` (every storefront), `iap` (in-app purchases), `versions` (version\nhistory with release notes), `privacy` (App Store privacy labels), `estimates` (modelled downloads /\nrevenue), `charts` (current top-chart positions), `summary` (AI summary, coming soon).\n",
        "x-credits": "1 + 1 per country refreshed from the store (max 10)",
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "name": "include",
            "in": "query",
            "description": "Comma list of extra sections.",
            "schema": {
              "type": "string",
              "example": "versions,iap"
            }
          },
          {
            "$ref": "#/components/parameters/MaxAgeApp"
          },
          {
            "$ref": "#/components/parameters/Wait"
          },
          {
            "$ref": "#/components/parameters/Fields"
          }
        ],
        "responses": {
          "200": {
            "description": "The app.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/App"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "data": {
                    "store": "apple",
                    "app_id": "324684580",
                    "bundle_id": "com.spotify.client",
                    "title": "Spotify: Music and Podcasts",
                    "developer": {
                      "id": "324684583",
                      "name": "Spotify"
                    },
                    "rating": 4.78,
                    "ratings_count": 42668141,
                    "version": "9.1.92",
                    "markets": [
                      {
                        "country": "us",
                        "available": true,
                        "price": 0,
                        "currency": "USD",
                        "rating": 4.78,
                        "ratings_count": 42668141,
                        "version": "9.1.92",
                        "fetched_at": "2026-10-22T15:03:07Z"
                      }
                    ],
                    "linked": {
                      "store": "play",
                      "app_id": "com.spotify.music",
                      "title": "Spotify: Music and Podcasts",
                      "icon_url": "https://…",
                      "match": "developer"
                    }
                  },
                  "meta": {
                    "credits_used": 1,
                    "credits_remaining": 9981,
                    "store": "apple",
                    "freshness": {
                      "fetched_at": "2026-10-22T15:03:07Z",
                      "age_seconds": 3600,
                      "stale": false,
                      "refreshing": false,
                      "job_id": null
                    },
                    "request_id": "req_123"
                  }
                }
              }
            }
          },
          "202": {
            "$ref": "#/components/responses/Fetching"
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/apps/{app}/reviews": {
      "get": {
        "operationId": "listReviews",
        "tags": [
          "Apps"
        ],
        "summary": "List an app's user reviews, newest first, with filters (rating, version, date, text).",
        "description": "Up to 100 reviews per page (1 credit per page). Combine `rating=1-2` with `since` to find recent\ncomplaints, `q` for full-text search, `version` to see what users say about a release. The latest US\nreviews are refreshed when older than `max_age` (default 1 day). AI tags (`sentiment`, `intent`,\n`severity`, `topic`) are filled for tagged reviews (popular apps first) and null otherwise; filtering by\nthem excludes untagged reviews. `intent=bug,crash&since=…` is the \"what broke in the last release\" query.\n",
        "x-credits": "1 per page + 1 if the US reviews are refreshed",
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "name": "rating",
            "in": "query",
            "description": "Star rating filter, `1`, `1,2` or a range `1-3`.",
            "schema": {
              "type": "string",
              "example": "1-2"
            }
          },
          {
            "name": "version",
            "in": "query",
            "description": "Only reviews written for this app version.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Since"
          },
          {
            "name": "until",
            "in": "query",
            "description": "Only reviews created before this ISO date / datetime.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Case-insensitive text search in the title and body.",
            "schema": {
              "type": "string",
              "example": "crash"
            }
          },
          {
            "name": "sentiment",
            "in": "query",
            "description": "AI sentiment filter (comma list): positive, neutral, negative.",
            "schema": {
              "type": "string",
              "example": "negative"
            }
          },
          {
            "name": "intent",
            "in": "query",
            "description": "AI intent filter (comma list): bug, crash, feature_request, pricing, ux, praise, spam, other.",
            "schema": {
              "type": "string",
              "example": "bug,crash"
            }
          },
          {
            "name": "topic",
            "in": "query",
            "description": "AI topic filter (comma list of topic labels, case-insensitive, or topic ids).",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/MaxAgeReviews"
          },
          {
            "$ref": "#/components/parameters/Wait"
          },
          {
            "$ref": "#/components/parameters/Limit100"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Fields"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of reviews.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Review"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/apps/{app}/history": {
      "get": {
        "operationId": "getAppHistory",
        "tags": [
          "Apps"
        ],
        "summary": "Get an app's daily metric history (rating, ratings count, installs, star histogram, price).",
        "description": "One row per country and day **on which a value changed** — carry the previous row forward for the days\nin between. Defaults to the last 90 days. Use it to chart growth or spot rating drops.\n",
        "x-credits": "1 + 1 per country refreshed (max 10)",
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "name": "from",
            "in": "query",
            "description": "Start date (ISO), default 90 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "End date (ISO), default today.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/MaxAgeApp"
          },
          {
            "$ref": "#/components/parameters/Wait"
          }
        ],
        "responses": {
          "200": {
            "description": "Metric rows, by country then date.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/HistoryPoint"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/apps/{app}/changes": {
      "get": {
        "operationId": "listAppChanges",
        "tags": [
          "Apps"
        ],
        "summary": "List what changed in an app's store listing (version, title, subtitle, screenshots, price, IAP, …).",
        "description": "Typed change feed, newest first: one entry per changed field with `old` and `new` values. Fields include\nversion, release_notes, title, subtitle, description, screenshots, icon, price, iap, iap_range, privacy,\nin_app_events, badges, featured, editors_choice, installs, content_rating, developer. Ideal for competitor\nmonitoring (\"what did X change this month?\").\n",
        "x-credits": "1 + 1 if the US listing is refreshed",
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "name": "country",
            "in": "query",
            "description": "Only changes seen in these storefronts (comma list). Default all storefronts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field",
            "in": "query",
            "description": "Only these fields (comma list), e.g. `version,title,subtitle`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Since"
          },
          {
            "$ref": "#/components/parameters/MaxAgeApp"
          },
          {
            "$ref": "#/components/parameters/Wait"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Changes, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Change"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/apps/{app}/keywords": {
      "get": {
        "operationId": "listAppKeywords",
        "tags": [
          "Apps"
        ],
        "summary": "List the search keywords an app ranks for, best rank first, with popularity and difficulty.",
        "description": "From the latest ranking we hold for each term: our keyword universe (re-ranked weekly, top 50) plus your\nown tracked keywords (`source: tracked`). Use it for ASO audits and to find keyword gaps against a\ncompetitor (call it for both apps and compare).\n",
        "x-credits": 1,
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "name": "max_rank",
            "in": "query",
            "description": "Only terms where the app ranks at or above this position.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked terms.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/AppKeyword"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/apps/{app}/competitors": {
      "get": {
        "operationId": "listCompetitors",
        "tags": [
          "Apps"
        ],
        "summary": "Find an app's competitors (store \"similar apps\" plus apps ranking for the same keywords).",
        "description": "Scored list: `shared_keywords` (search terms both apps rank for) counts most, the store's own similar-apps\nlist (`similar_position`) adds a boost. Feed the result into `getApp` / `listAppChanges` for a\ncompetitor digest.\n",
        "x-credits": 1,
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Competitors, best first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Competitor"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/apps/{app}/insights": {
      "get": {
        "operationId": "getAppInsights",
        "tags": [
          "Apps"
        ],
        "summary": "Review intelligence for an app - top issues with trends and samples, topics per version, weekly sentiment.",
        "description": "Built from the AI tags on every review (sentiment, intent, severity, topic). `top_issues` are topics carried\nby negative / bug / crash / UX / pricing reviews, with `last_30d` vs `prev_30d` counts and sample reviews;\n`versions` shows what each release changed in sentiment and topics; `sentiment_over_time` is weekly. Use it\nfor \"what do users complain about since v2.3?\" or a reviews → GitHub issues playbook. Reviews not tagged yet\nonly count in the totals (`tagged` < `reviews`, see `meta.warnings`).\n",
        "x-credits": 25,
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "name": "country",
            "in": "query",
            "description": "One storefront; omit for every storefront we hold reviews for.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since_days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 7,
              "maximum": 730,
              "default": 180
            }
          },
          {
            "name": "versions",
            "in": "query",
            "description": "How many of the latest versions to break down.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Insights.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Insights"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/apps/{app}/audit": {
      "post": {
        "operationId": "runAppAudit",
        "tags": [
          "Audits"
        ],
        "summary": "Run an App Audit - prioritised ASO actions, metadata rewrites and findings with evidence (first one free).",
        "description": "Refreshes the listing, tags recent reviews, probes ~20 search terms (popularity, difficulty, the app's\nrank, relevance), compares competitors, then writes a prioritised report: `report.actions` (what to do\nfirst, with the evidence and a playbook), `report.metadata` (title / subtitle / keyword-field rewrites\nwithin the character limits) and `report.findings` (deterministic checks with numbers). Every statement\nin the narrative is checked against the measured facts; unsupported ones are dropped.\nTakes 1-3 minutes: with `wait=true` (default) the call blocks up to ~25 s, then answers 202 with the audit\n`id`; poll `GET /v1/audits/{id}` (free). The shareable page is `url` (appgoat.ai/r/..., not indexed).\nAn audit of the same app, country and mode from the last 24 h is returned again for 0 credits unless\n`fresh=true`.\nFailed audits are never charged: one that fails within `wait` costs 0, one that fails after the 202 is\nrefunded automatically (the refund shows on the same request in your usage logs).\n",
        "x-credits": "100 (the first audit of an organization is free)",
        "parameters": [
          {
            "$ref": "#/components/parameters/App"
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "country": {
                    "type": "string",
                    "default": "us",
                    "description": "One storefront."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "full",
                      "lite"
                    ],
                    "default": "full",
                    "description": "lite = cached data, fewer reviews and keywords (cheaper and faster for us; same price)."
                  },
                  "wait": {
                    "type": "boolean",
                    "default": true
                  },
                  "fresh": {
                    "type": "boolean",
                    "default": false,
                    "description": "Run again even if a recent audit exists."
                  },
                  "free_only": {
                    "type": "boolean",
                    "default": false,
                    "description": "Only run when it is the free first audit (402 free_audit_used otherwise)."
                  },
                  "white_label": {
                    "type": "boolean",
                    "default": false,
                    "description": "Studio: the report page carries your branding (brand name, logo, accent colour, CTA, footer; set them in the dashboard under Settings → Report branding) instead of AppGoat's. 402 plan_limit on other plans. Re-using a recent audit applies the branding to it for 0 credits."
                  },
                  "label": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 80,
                    "description": "Your label for the audit list, e.g. the client or prospect name."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The finished audit (or a recent one re-used).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Audit"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Still running; poll `GET /v1/audits/{data.id}`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Audit"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/search": {
      "get": {
        "operationId": "searchApps",
        "tags": [
          "Search & charts"
        ],
        "summary": "Search the catalog (~5M apps in both stores) by app name or id.",
        "description": "Exact title matches first, then prefix, then substring; most ratings first within each group.",
        "x-credits": 1,
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "App name (≥ 2 characters) or exact id.",
            "schema": {
              "type": "string",
              "example": "photo editor"
            }
          },
          {
            "name": "store",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Store"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Store category name, e.g. `Photo & Video` (App Store) or `PHOTOGRAPHY` (Play).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Fields"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching apps.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/AppCard"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/charts": {
      "get": {
        "operationId": "getChart",
        "tags": [
          "Search & charts"
        ],
        "summary": "Get a top chart (free, paid, grossing) for a store, country and category, today or on a past date.",
        "description": "Entries carry `delta` (positions gained since the previous changed capture; negative = dropped) and\n`is_new`; `dropped` lists apps that fell out. Charts are captured daily for every country × category.\n",
        "x-credits": 1,
        "parameters": [
          {
            "name": "store",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Store",
              "default": "apple"
            }
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "us"
            }
          },
          {
            "name": "chart",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "free",
                "paid",
                "grossing"
              ],
              "default": "free"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "App Store genre id (`36` or `all` = all apps, `6014` = games, …) or Play category (`GAME_PUZZLE`; `APPLICATION` = all apps).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "description": "The chart as of this date (ISO). Default the latest capture.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The chart.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Chart"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/keywords": {
      "get": {
        "operationId": "getKeywords",
        "tags": [
          "Keywords"
        ],
        "summary": "Get keyword metrics (popularity, difficulty) for up to 25 terms; with include=related, keyword research.",
        "description": "`popularity` is 5–100 (search volume proxy; `popularity_source: apple` = Apple Search Ads data),\n`difficulty` 0–100 (how entrenched the top 10 is), `est_downloads_at_1` the modelled daily downloads of the\n#1 result. Terms not measured in the last 7 days are measured live (≤ 5 per call, 2–5 s each) when\n`wait=true`; others return `pending: true` — call again in a minute.\n`include=related` adds ~10 related terms with their metrics, `suggestions` the store's autocomplete,\n`top_apps` the current top 10 apps. Pass `app=` to get that app's rank and a metadata `relevance` (0–1) for\nevery term — the core of an ASO keyword audit.\n",
        "x-credits": "1 per keyword; 5 per seed with include=related or suggestions",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "One term. Use `qs` for several.",
            "schema": {
              "type": "string",
              "example": "photo editor"
            }
          },
          {
            "name": "qs",
            "in": "query",
            "description": "Comma-separated terms (≤ 25; ≤ 5 with related / suggestions).",
            "schema": {
              "type": "string",
              "example": "photo editor,collage maker"
            }
          },
          {
            "name": "store",
            "in": "query",
            "description": "Required unless `app` is given.",
            "schema": {
              "$ref": "#/components/schemas/Store"
            }
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "us"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "Comma list: related, suggestions, top_apps.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "app",
            "in": "query",
            "description": "Any app identifier (as in `{app}`); adds `app.rank` and `app.relevance` per term.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Wait"
          }
        ],
        "responses": {
          "200": {
            "description": "One entry per term, in request order.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Keyword"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "data": [
                    {
                      "term": "photo editor",
                      "store": "apple",
                      "country": "us",
                      "popularity": 62,
                      "popularity_source": "proxy",
                      "difficulty": 81,
                      "results_count": 200,
                      "est_downloads_at_1": 2900,
                      "measured_at": "2026-10-20T10:00:00Z",
                      "pending": false,
                      "top_app_ids": [
                        "1",
                        "2"
                      ],
                      "app": {
                        "store": "apple",
                        "app_id": "1",
                        "rank": 14,
                        "relevance": 1
                      }
                    }
                  ],
                  "meta": {
                    "credits_used": 1,
                    "store": "apple"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/tracked-keywords": {
      "get": {
        "operationId": "listTrackedKeywords",
        "deprecated": true,
        "tags": [
          "Keywords"
        ],
        "summary": "List your tracked keywords with their latest top 10.",
        "x-credits": 0,
        "parameters": [
          {
            "name": "store",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Store"
            }
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Substring filter on the term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tracked keywords.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/TrackedKeyword"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "trackKeywords",
        "deprecated": true,
        "tags": [
          "Keywords"
        ],
        "summary": "Start tracking search ranks for keywords (daily re-rank; idempotent).",
        "description": "Each (term × store × country) uses one keyword slot of your plan; a request that would exceed it tracks nothing and returns 402 plan_limit.",
        "x-credits": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "store"
                ],
                "properties": {
                  "term": {
                    "type": "string"
                  },
                  "terms": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 500
                  },
                  "store": {
                    "type": "string",
                    "enum": [
                      "apple",
                      "play",
                      "both"
                    ]
                  },
                  "countries": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "default": [
                      "us"
                    ]
                  },
                  "depth": {
                    "type": "integer",
                    "description": "How many results to keep per ranking (default 100 Apple / 50 Play)."
                  },
                  "cadenceMinutes": {
                    "type": "integer",
                    "description": "Minutes between re-ranks (paid plans can go below 1440)."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "terms": [
                  "photo editor",
                  "collage maker"
                ],
                "store": "both",
                "countries": [
                  "us",
                  "gb"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "One row per tracked (store, country, term).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "store": {
                                "$ref": "#/components/schemas/Store"
                              },
                              "country": {
                                "type": "string"
                              },
                              "term": {
                                "type": "string"
                              },
                              "created": {
                                "type": "boolean",
                                "description": "false = you already tracked it."
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/tracked-keywords/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TrackedKeywordId"
        }
      ],
      "get": {
        "operationId": "getTrackedKeyword",
        "deprecated": true,
        "tags": [
          "Keywords"
        ],
        "summary": "Get one tracked keyword and its latest top 10.",
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "The tracked keyword.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/TrackedKeyword"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateTrackedKeyword",
        "deprecated": true,
        "tags": [
          "Keywords"
        ],
        "summary": "Change a tracked keyword's depth, cadence or tags.",
        "x-credits": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "depth": {
                    "type": "integer"
                  },
                  "cadenceMinutes": {
                    "type": "integer"
                  },
                  "tags": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated tracked keyword.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/TrackedKeyword"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "untrackKeyword",
        "deprecated": true,
        "tags": [
          "Keywords"
        ],
        "summary": "Stop tracking a keyword (frees its slot).",
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "deleted": {
                              "type": "boolean"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/tracked-keywords/{id}/ranks": {
      "get": {
        "operationId": "getTrackedKeywordRanks",
        "tags": [
          "Keywords"
        ],
        "summary": "Get a tracked keyword's ranking history (or one app's rank over time with app=).",
        "x-credits": 1,
        "parameters": [
          {
            "$ref": "#/components/parameters/TrackedKeywordId"
          },
          {
            "name": "from",
            "in": "query",
            "description": "Default 90 days ago.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "app",
            "in": "query",
            "description": "Store app id; returns `rank` per point instead of the full id list.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One point per capture.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "changed": {
                                "type": "boolean"
                              },
                              "rank": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "appIds": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/tracked-keywords/{id}/run": {
      "post": {
        "operationId": "runTrackedKeyword",
        "tags": [
          "Keywords"
        ],
        "summary": "Re-rank a tracked keyword now instead of waiting for the next scheduled run (paid plans).",
        "description": "If it was ranked within the last hour you get that ranking back (200, `queued:false`, free) instead.",
        "x-credits": "1 when queued, 0 when the last hour's ranking is returned",
        "parameters": [
          {
            "$ref": "#/components/parameters/TrackedKeywordId"
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked recently; the latest ranking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developers/{dev}": {
      "get": {
        "operationId": "getDeveloper",
        "tags": [
          "Developers"
        ],
        "summary": "Get a developer's profile, all their apps and the same company in the other store.",
        "description": "Name, store page, website and domain, country, app count, total ratings and installs, top category, their apps (most ratings first) and the linked developer in the other store.",
        "x-credits": 1,
        "parameters": [
          {
            "name": "dev",
            "in": "path",
            "required": true,
            "description": "`apple:<artist id>`, `play:<developer id>`, a numeric Apple artist id, a Play developer id (often the name), or the website domain (`spotify.com`).",
            "schema": {
              "type": "string",
              "example": "apple:324684583"
            }
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "$ref": "#/components/parameters/Fields"
          }
        ],
        "responses": {
          "200": {
            "description": "The developer.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Developer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/audits": {
      "get": {
        "operationId": "listAudits",
        "tags": [
          "Audits"
        ],
        "summary": "List your organization's App Audits, newest first, with status and score (free).",
        "x-credits": 0,
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Audits.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/AuditSummary"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/audits/{id}": {
      "get": {
        "operationId": "getAudit",
        "tags": [
          "Audits"
        ],
        "summary": "Get one App Audit by id - poll this after POST /v1/apps/{app}/audit answered 202 (free).",
        "description": "`status`: queued, running, batched (narrative queued in the batch API), done or failed. `include=facts` adds the measured facts the report is built from.",
        "x-credits": 0,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "aud_3fK9aQ2mXc81LpRt"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "facts"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The audit.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Audit"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateAudit",
        "tags": [
          "Audits"
        ],
        "summary": "White-label an App Audit report with your branding (Studio) or switch it back, and set its label (free).",
        "description": "`white_label: true` re-applies your current branding (use it after changing the branding), `false` shows the standard AppGoat report. 402 plan_limit for white_label on plans without white-label audits.",
        "x-credits": 0,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "aud_3fK9aQ2mXc81LpRt"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "white_label": {
                    "type": "boolean"
                  },
                  "label": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 80
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated audit.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Audit"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/watches": {
      "get": {
        "operationId": "listWatches",
        "tags": [
          "Watches & events"
        ],
        "summary": "List your watches (apps, competitor sets, keyword sets) with usage against your plan's limits.",
        "description": "`meta.usage` has watched apps and keyword slots used vs your plan (`null` limit = unlimited).",
        "x-credits": 0,
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/WatchKind"
            }
          },
          {
            "name": "app",
            "in": "query",
            "description": "Only watches that include this app (any `{app}` input).",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          }
        ],
        "responses": {
          "200": {
            "description": "Watches, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Watch"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          }
        }
      },
      "post": {
        "operationId": "createWatch",
        "tags": [
          "Watches & events"
        ],
        "summary": "Watch an app, a set of competitors or a set of keywords and get events when something significant changes.",
        "description": "Use this when the user wants to be told about changes (\"tell me when a competitor updates\", \"alert me when my\nrating drops\", \"track my ranks for these keywords\"). Kinds:\n- `app` { app }: new versions, metadata / price changes, featuring, rating drops, review and ratings spikes,\n  chart entries for that app.\n- `competitor_set` { app?, competitors[] }: the same for every app in the set (your own `app` optional).\n- `keyword` { terms[], store, app? }: search-rank moves of `app` for each term (`rank_change`) and new apps in\n  the top N (`keyword_entrant`).\nWatched apps (competitors included) count against `limits.watches`; keyword watches use one keyword slot per\nterm × store × country (`limits.keywords`). Exceeding either returns 402 plan_limit and changes nothing.\nWatched apps are refreshed daily. Events appear at `GET /v1/events` and go to your webhooks.\n",
        "x-credits": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WatchInput"
              },
              "examples": {
                "app": {
                  "value": {
                    "kind": "app",
                    "app": "com.spotify.music",
                    "countries": [
                      "us",
                      "de"
                    ]
                  }
                },
                "competitors": {
                  "value": {
                    "kind": "competitor_set",
                    "app": "284882215",
                    "competitors": [
                      "389801252",
                      "com.zhiliaoapp.musically"
                    ],
                    "event_types": [
                      "new_version",
                      "metadata_change",
                      "price_change"
                    ]
                  }
                },
                "keywords": {
                  "value": {
                    "kind": "keyword",
                    "app": "284882215",
                    "terms": [
                      "photo editor",
                      "collage maker"
                    ],
                    "store": "apple",
                    "countries": [
                      "us",
                      "gb"
                    ],
                    "settings": {
                      "rank_move": 3
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The watch.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Watch"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/AppNotFound"
          }
        }
      }
    },
    "/watches/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WatchId"
        }
      ],
      "get": {
        "operationId": "getWatch",
        "tags": [
          "Watches & events"
        ],
        "summary": "Get one watch with its apps, keyword slots, thresholds and event types.",
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "The watch.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Watch"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateWatch",
        "tags": [
          "Watches & events"
        ],
        "summary": "Change a watch (apps, competitors, terms, countries, event types, thresholds) or pause it with active=false.",
        "description": "Send only what changes; `settings` is merged into the current settings. Growing a watch re-checks your plan's limits (402 plan_limit).",
        "x-credits": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WatchPatch"
              },
              "example": {
                "competitors": [
                  "389801252",
                  "com.instagram.android"
                ],
                "settings": {
                  "significance": 0.7
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated watch.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Watch"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteWatch",
        "tags": [
          "Watches & events"
        ],
        "summary": "Stop watching (frees its apps and keyword slots; past events are kept).",
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "deleted": {
                              "type": "boolean"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Watches & events"
        ],
        "summary": "List significant changes on your watched apps and keywords, newest first (free; poll this to wake an agent).",
        "description": "Each event has a deterministic one-line `title`, an AI `summary` (or null), a `significance` (0–1) and a typed\n`payload`. Only changes that reach the watch's `settings.significance` become events. To poll, pass\n`since=` the `created_at` of the newest event you already handled (or remember its id and stop paging there).\n",
        "x-credits": 0,
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Comma list of event types.",
            "schema": {
              "type": "string",
              "example": "new_version,rating_drop"
            }
          },
          {
            "name": "app",
            "in": "query",
            "description": "Only events of this app (any `{app}` input).",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/StoreHint"
          },
          {
            "name": "watch",
            "in": "query",
            "description": "Only events of this watch id.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/Since"
          },
          {
            "name": "min_significance",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Events.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Event"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "summary": "List your event endpoints (signed webhooks, Slack incoming webhooks, GitHub repository_dispatch).",
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "Endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookEndpoint"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Send events to a URL, a Slack channel or a GitHub repository (repository_dispatch) as they happen (paid plans).",
        "description": "`kind`:\n- `webhook` (default): POST of the event JSON, signed with `appgoat-signature: t=<unix>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>`.\n  The `secret` is returned once, here (rotate with PATCH rotate_secret). Answer 2xx within 10 s.\n- `slack`: `url` is a Slack incoming webhook (https://hooks.slack.com/services/…).\n- `github`: `url` is `owner/repo`, `github_token` a fine-grained token with Contents: read and write on it.\n  Sends `{ event_type: \"appgoat.<type>\", client_payload: <event> }` to start a workflow `on: repository_dispatch`.\nFailed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h; an endpoint failing 50 times\nin a row is disabled (PATCH active=true re-enables it). Free plan: 402 plan_limit (use GET /v1/events).\n",
        "x-credits": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "https URL (webhook), Slack incoming webhook URL (slack) or owner/repo (github)."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "webhook",
                      "slack",
                      "github"
                    ],
                    "default": "webhook"
                  },
                  "event_types": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "$ref": "#/components/schemas/EventType"
                    },
                    "description": "null = every type."
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "github_token": {
                    "type": "string",
                    "description": "kind github only; never returned."
                  }
                }
              },
              "examples": {
                "webhook": {
                  "value": {
                    "url": "https://example.com/hooks/appgoat",
                    "event_types": [
                      "new_version",
                      "rating_drop"
                    ]
                  }
                },
                "github": {
                  "value": {
                    "kind": "github",
                    "url": "acme/my-app",
                    "github_token": "github_pat_xxx",
                    "event_types": [
                      "negative_review_spike"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The endpoint, with its signing `secret` (only now).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookEndpoint"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookId"
        }
      ],
      "get": {
        "operationId": "getWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Get one event endpoint with its delivery health (failure count, last success / failure).",
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookEndpoint"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Change an endpoint's URL, event types or description, re-enable it, or rotate its signing secret.",
        "x-credits": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "event_types": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "$ref": "#/components/schemas/EventType"
                    }
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "github_token": {
                    "type": "string"
                  },
                  "rotate_secret": {
                    "type": "boolean",
                    "description": "true = new secret, returned in this response only."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookEndpoint"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete an event endpoint and its delivery log.",
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "deleted": {
                              "type": "boolean"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a `test` event to an endpoint now and see what it answered (status, body, latency).",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          }
        ],
        "responses": {
          "200": {
            "description": "The attempt (`ok` false when the endpoint did not answer 2xx).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "delivery_id": {
                              "type": "string"
                            },
                            "ok": {
                              "type": "boolean"
                            },
                            "response_status": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "response_body": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "error": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "duration_ms": {
                              "type": "integer"
                            },
                            "event": {
                              "$ref": "#/components/schemas/Event"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "tags": [
          "Webhooks"
        ],
        "summary": "List an endpoint's delivery log (every attempt's status, response and next retry), newest first.",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "succeeded",
                "failed"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookDelivery"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/connections": {
      "get": {
        "operationId": "listConnections",
        "tags": [
          "Connections"
        ],
        "summary": "List the connected App Store Connect, Play Console and Apple Ads accounts (ids, linked apps, status; never secrets).",
        "description": "Use the connection `id` in `POST /v1/actions` (or omit it when only one connection fits). `status: invalid`\nmeans the provider rejected the credentials since they were added (`last_error` says why); fix them and call\n`POST /v1/connections/{id}/verify`. `mode: read_only` connections can dry-run actions but not apply them.\n",
        "x-credits": 0,
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Provider"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The connections.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Connection"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createConnection",
        "tags": [
          "Connections"
        ],
        "summary": "Connect an App Store Connect, Play Console or Apple Ads account (credentials are verified, then stored encrypted).",
        "description": "Credentials per provider (all are verified with one cheap read before anything is stored; they are encrypted\nwith AES-256-GCM and never returned):\n- `asc`: `{ issuer_id, key_id, private_key, vendor_number? }` from App Store Connect → Users and Access →\n  Integrations → App Store Connect API (role App Manager; Sales too for downloads/proceeds sync with\n  `vendor_number` from Payments and Financial Reports). `apps` defaults to every app the key can see.\n- `play_console`: `{ service_account, reports_bucket? }` = the Google Cloud service account JSON key, invited in\n  Play Console → Users and permissions with View app information, Reply to reviews and Edit store listing.\n  `apps` (package names) is required. `reports_bucket` (pubsite_prod_…) enables install stats.\n- `apple_ads`: `{ client_id, team_id, key_id, private_key, ad_account_id? }` from Apple Ads → Account Settings →\n  API for an API user with the API Account Manager role (Apple Ads Platform API; `ad_account_id` is needed when\n  the user can access several ad accounts, the error lists them).\nEach linked app uses one of the plan's connected apps (`limits.connections`; Free 1, read-only). The default\nmode is `actions` (`read_only` on Free).\n",
        "x-credits": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "provider",
                  "credentials"
                ],
                "properties": {
                  "provider": {
                    "$ref": "#/components/schemas/Provider"
                  },
                  "label": {
                    "type": "string",
                    "description": "Your name for it (default: provider + key / account)."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "actions",
                      "read_only"
                    ]
                  },
                  "credentials": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "See the description for the fields per provider."
                  },
                  "apps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Apps to link: App Store ids / bundle ids (asc, apple_ads) or package names (play_console, required)."
                  }
                }
              },
              "example": {
                "provider": "asc",
                "label": "My ASC key",
                "credentials": {
                  "issuer_id": "57246542-96fe-1a63-e053-0824d011072a",
                  "key_id": "2X9R4HXF34",
                  "private_key": "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----"
                },
                "apps": [
                  "1234567890"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Connected and verified.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Connection"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "422": {
            "$ref": "#/components/responses/InvalidCredentials"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/connections/{id}": {
      "get": {
        "operationId": "getConnection",
        "tags": [
          "Connections"
        ],
        "summary": "Get one connection (status, linked apps, non-secret account identifiers).",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/ConnectionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The connection.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Connection"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateConnection",
        "tags": [
          "Connections"
        ],
        "summary": "Rename a connection, switch it between read_only and actions, or change which apps it links.",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/ConnectionId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "actions",
                      "read_only"
                    ]
                  },
                  "apps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Replaces the linked apps (checked against what the credentials can access)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Connection"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteConnection",
        "tags": [
          "Connections"
        ],
        "summary": "Disconnect an account; its credentials are erased and open (draft / pending) actions are rejected.",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/ConnectionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/connections/{id}/verify": {
      "post": {
        "operationId": "verifyConnection",
        "tags": [
          "Connections"
        ],
        "summary": "Re-check a connection's credentials with the provider (after fixing a role or key); lists every app it can access.",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/ConnectionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Verified; `data.available_apps` are the apps the credentials can see (link more with PATCH).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Connection"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/InvalidCredentials"
          }
        }
      }
    },
    "/connections/{id}/reviews": {
      "get": {
        "operationId": "listConnectionReviews",
        "tags": [
          "Connections"
        ],
        "summary": "Read your own app's reviews from App Store Connect / Play Console with reply status; their ids feed review.reply.",
        "description": "`unanswered=true` keeps reviews without a developer reply (the \"reply to every review\" playbook). Play only\nreturns reviews written or edited in the last 7 days (Google API limit); App Store Connect returns all.\n",
        "x-credits": 1,
        "parameters": [
          {
            "$ref": "#/components/parameters/ConnectionId"
          },
          {
            "name": "app",
            "in": "query",
            "required": true,
            "description": "A linked app (App Store id or package name).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "unanswered",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "rating",
            "in": "query",
            "description": "`1`, `1,2` or `1-3`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "country",
            "in": "query",
            "description": "App Store territory filter (ISO alpha-2).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 100
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of reviews.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ProviderReview"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/connections/{id}/listings": {
      "get": {
        "operationId": "getConnectionListing",
        "tags": [
          "Connections"
        ],
        "summary": "Read your app's current listing text for one locale and which fields can be edited right now.",
        "description": "App Store name/subtitle/description/keywords/what's new need a version in Prepare for Submission; promotional text is editable on the live version. Play fields are always editable.",
        "x-credits": 1,
        "parameters": [
          {
            "$ref": "#/components/parameters/ConnectionId"
          },
          {
            "name": "app",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "en-US"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The listing.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Listing"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/connections/{id}/ads": {
      "get": {
        "operationId": "getConnectionAds",
        "tags": [
          "Connections"
        ],
        "summary": "Read Apple Ads campaigns, ad groups, keywords (with bids), negatives, or a performance report.",
        "description": "`view=campaigns` → `ad_groups` (campaign_id) → `keywords` (campaign_id, ad_group_id) gives the ids the ads.*\nactions take. `view=report&level=keywords|searchterms|adgroups|campaigns&campaign_id=` returns impressions,\ntaps, installs, spend, avg CPT / CPA for `start`..`end` (default the last 7 days, UTC).\n",
        "x-credits": 1,
        "parameters": [
          {
            "$ref": "#/components/parameters/ConnectionId"
          },
          {
            "name": "view",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "campaigns",
                "ad_groups",
                "keywords",
                "negatives",
                "report"
              ],
              "default": "campaigns"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ad_group_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "level",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "campaigns",
                "adgroups",
                "keywords",
                "searchterms"
              ]
            }
          },
          {
            "name": "start",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "end",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The requested view.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/actions": {
      "get": {
        "operationId": "listActions",
        "tags": [
          "Actions"
        ],
        "summary": "List proposed and applied actions (drafts, pending approval, applied, failed, rejected), newest first.",
        "x-credits": 0,
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Comma list: draft, pending_approval, approved, applied, failed, rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ActionType"
            }
          },
          {
            "name": "connection",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit100"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of actions.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Action"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createAction",
        "tags": [
          "Actions"
        ],
        "summary": "Propose a write to a connected account (reply to a review, update a listing, set keywords, upload screenshots, change Apple Ads bids / keywords / negatives); always start with dry_run=true.",
        "description": "Computes the before → after `diff` from the live account and runs guardrails (policy / tone / claims\nprobabilities for text, plus deterministic checks like character limits and bid jumps). Free.\n- `dry_run: true` → a `draft` (24 h) with the diff: show it to the user. To go ahead, call\n  `POST /v1/actions/{id}/approve`.\n- otherwise → `pending_approval` (a person approves it in the dashboard, link in `approve_url`), or `applied`\n  immediately when the org has an auto-apply rule for this type that the guardrail score clears (Studio,\n  1 credit).\nBlocking check failures (over a character limit, nothing to change, not editable) return 400 with `checks`\n(a dry run returns them in `guardrail.checks_failed` instead).\n\nParams per type (`app` = a linked App Store id or package name, `locale` like en-US):\n- `review.reply` (asc, play_console): `{ app, review_id, text }` — review ids from GET /v1/connections/{id}/reviews. Play replies ≤ 350 chars.\n- `listing.update` (asc, play_console): `{ app, locale, fields: { name|title, subtitle, description, promotional_text, whats_new, marketing_url, support_url, privacy_policy_url } }` (Play: title, short_description, full_description, video).\n- `keywords.set` (asc): `{ app, locale, keywords: [..] | \"a,b,c\", mode?: replace|add|remove }` — 100 bytes comma-joined.\n- `screenshots.upload` (asc, play_console): `{ app, locale, display_type: APP_IPHONE_67 … (asc) | image_type: phoneScreenshots … (play), images: [{ url }], replace?: true }`.\n- `ads.keyword.bid` (apple_ads): `{ campaign_id, ad_group_id, keywords: [{ id, bid?, status?: ENABLED|PAUSED }], max_increase?: 1 }`.\n- `ads.keyword.add` (apple_ads): `{ campaign_id, ad_group_id, keywords: [{ text, match_type?: EXACT|BROAD, bid? }] }`.\n- `ads.negative.add` (apple_ads): `{ campaign_id, ad_group_id?, keywords: [{ text, match_type? }] }`.\n",
        "x-credits": "0 (dry run, pending approval) or 1 when auto-applied",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "params"
                ],
                "properties": {
                  "type": {
                    "$ref": "#/components/schemas/ActionType"
                  },
                  "connection": {
                    "type": "string",
                    "description": "Connection id; optional when exactly one active connection fits the type (and params.app)."
                  },
                  "params": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "dry_run": {
                    "type": "boolean",
                    "default": false
                  },
                  "source": {
                    "type": "string",
                    "description": "Who proposes it: api, mcp or playbook:<name> (shown in the approvals list)."
                  }
                }
              },
              "example": {
                "type": "review.reply",
                "dry_run": true,
                "source": "playbook:reply-to-every-review",
                "params": {
                  "app": "1234567890",
                  "review_id": "00000043-a2fa-4f01-0000-000000000000",
                  "text": "Thanks for the detailed report! Version 2.4.1 fixes the login loop."
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The action (draft, pending_approval or applied).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Action"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidParam"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/actions/{id}": {
      "get": {
        "operationId": "getAction",
        "tags": [
          "Actions"
        ],
        "summary": "Get one action with its diff, guardrail scores, status, result or error, and audit trail.",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/ActionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The action.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Action"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/actions/{id}/approve": {
      "post": {
        "operationId": "approveAction",
        "tags": [
          "Actions"
        ],
        "summary": "Go ahead with a draft or pending action; applies it when a person approves (or a rule allows), else submits it for approval.",
        "description": "From an API key / agent: a `draft` becomes `pending_approval` (a person approves at `approve_url`) unless an\nauto-apply rule clears it, in which case it is applied. A `pending_approval` action needs a person unless the\norg enabled agent approval (403 otherwise). From the dashboard: applies it. The diff is recomputed before\napplying; 409 conflict when the account changed since the dry run (`force: true` applies the new diff).\nThe result is `applied` (1 credit) or `failed` with `error` (free).\n",
        "x-credits": "1 when applied, else 0",
        "parameters": [
          {
            "$ref": "#/components/parameters/ActionId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "force": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The action after approval (pending_approval, applied or failed).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Action"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/actions/{id}/reject": {
      "post": {
        "operationId": "rejectAction",
        "tags": [
          "Actions"
        ],
        "summary": "Discard a draft or pending action (nothing is written to the account).",
        "x-credits": 0,
        "parameters": [
          {
            "$ref": "#/components/parameters/ActionId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rejected.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Action"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/jobs/{id}": {
      "get": {
        "operationId": "getJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Poll a background store fetch started by a read that answered refreshing:true (free).",
        "description": "Poll every few seconds until `status` is done / partial / failed, then repeat the original call.",
        "x-credits": 0,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`meta.freshness.job_id`, e.g. `fetch_123`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenapi",
        "tags": [
          "Account"
        ],
        "summary": "This OpenAPI 3.1 document (no key needed, free).",
        "security": [],
        "x-credits": 0,
        "responses": {
          "200": {
            "description": "The spec.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "Your plan, credits left this cycle and limits.",
        "x-credits": 0,
        "x-served-by": "gateway",
        "responses": {
          "200": {
            "description": "The account.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "event": {
      "post": {
        "operationId": "receiveEvent",
        "summary": "What AppGoat POSTs to your `kind: webhook` endpoint for every event (and for test sends, type `test`).",
        "description": "Verify `appgoat-signature: t=<unix seconds>,v1=<hex>` before trusting the body: compute\nHMAC-SHA256(secret, `<t>.<raw request body>`), compare in constant time, and reject timestamps older than\n5 minutes. Deduplicate on `appgoat-event-id` (a retry repeats it). Answer 2xx within 10 seconds; do the\nwork asynchronously.\n",
        "parameters": [
          {
            "name": "appgoat-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1792650000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "appgoat-event-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "appgoat-event-type",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "appgoat-delivery-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery; anything else (or a timeout) is retried."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Create a key at https://dash.appgoat.ai."
      }
    },
    "parameters": {
      "App": {
        "name": "app",
        "in": "path",
        "required": true,
        "description": "Any app identifier: App Store id (`284882215`, `id284882215`), Play package (`com.spotify.music`), Apple bundle id (`com.spotify.client`), store URL (URL-encoded), `apple:<id>` / `play:<id>`, or a name (`spotify`).",
        "schema": {
          "type": "string",
          "example": "com.spotify.music"
        }
      },
      "StoreHint": {
        "name": "store",
        "in": "query",
        "description": "Disambiguates `{app}` (a bundle id vs a Play package, or a name).",
        "schema": {
          "$ref": "#/components/schemas/Store"
        }
      },
      "Country": {
        "name": "country",
        "in": "query",
        "description": "ISO country code(s): `us` (default), a comma list (paid plans), or `all` (Studio).",
        "schema": {
          "type": "string",
          "default": "us",
          "example": "us,de"
        }
      },
      "MaxAgeApp": {
        "name": "max_age",
        "in": "query",
        "description": "Refresh from the store when our data is older than this many seconds (0 = always). Default 604800 (7 days).",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 604800
        }
      },
      "MaxAgeReviews": {
        "name": "max_age",
        "in": "query",
        "description": "Refresh the latest US reviews when older than this many seconds. Default 86400 (1 day).",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 86400
        }
      },
      "Wait": {
        "name": "wait",
        "in": "query",
        "description": "Block up to ~25 s for a refresh (default). false = answer immediately from cache with a job id.",
        "schema": {
          "type": "boolean",
          "default": true
        }
      },
      "Since": {
        "name": "since",
        "in": "query",
        "description": "Only items at or after this ISO date / datetime.",
        "schema": {
          "type": "string",
          "format": "date-time",
          "example": "2026-10-01"
        }
      },
      "Limit100": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "`meta.next_cursor` from the previous page.",
        "schema": {
          "type": "string"
        }
      },
      "Fields": {
        "name": "fields",
        "in": "query",
        "description": "Comma list of top-level fields to return (identifiers are always kept), e.g. `title,rating,version`.",
        "schema": {
          "type": "string"
        }
      },
      "ConnectionId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Connection id (`con_…`).",
        "schema": {
          "type": "string"
        }
      },
      "ActionId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Action id (`act_…`).",
        "schema": {
          "type": "string"
        }
      },
      "TrackedKeywordId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      },
      "WatchId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      },
      "WebhookId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Fetching": {
        "description": "Not in the catalog yet and still being fetched; poll `GET /v1/jobs/{meta.freshness.job_id}` and call again.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            }
          }
        }
      },
      "InvalidParam": {
        "description": "A parameter is missing or invalid (`error.param` names it).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "invalid_param",
                "message": "limit must be ≤ 100",
                "hint": "Fix the parameter and retry.",
                "docs": "https://appgoat.ai/docs/errors#invalid_param",
                "param": "limit"
              }
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "`insufficient_credits` (the call costs more than you have left; nothing was done) or `plan_limit` (a feature of a higher plan).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "AppNotFound": {
        "description": "Nothing matches `{app}`, or the store does not have it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InvalidCredentials": {
        "description": "The provider rejected the credentials, or a field is missing; `hint` says where to find / fix it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ProviderError": {
        "description": "App Store Connect / Google Play / Apple Ads answered with an error (message passed through).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "The state does not allow this (already applied, expired, connection read-only or invalid, account changed since the dry run).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Needs a person (approve in the dashboard at `error.approve_url`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Provider": {
        "type": "string",
        "enum": [
          "asc",
          "play_console",
          "apple_ads"
        ],
        "description": "asc = App Store Connect, play_console = Google Play Console, apple_ads = Apple Ads (Search Ads)."
      },
      "LinkedApp": {
        "type": "object",
        "properties": {
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "app_id": {
            "type": "string"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "bundle_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Connection": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "con_x8Hq2mXc0aLpQwRtYb"
          },
          "provider": {
            "$ref": "#/components/schemas/Provider"
          },
          "label": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "actions",
              "read_only"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "invalid",
              "deleted"
            ]
          },
          "account": {
            "type": "object",
            "additionalProperties": true,
            "description": "Non-secret identifiers (issuer_id, key_id, client_email, ad_account_id, ad_account_name, currency, …)."
          },
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkedApp"
            }
          },
          "last_verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_synced_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last downloads / proceeds sync (ASC with vendor_number",
            "Play with reports_bucket).": null
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "available_apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkedApp"
            },
            "description": "Only on verify."
          }
        }
      },
      "ActionType": {
        "type": "string",
        "enum": [
          "review.reply",
          "listing.update",
          "keywords.set",
          "screenshots.upload",
          "ads.keyword.bid",
          "ads.keyword.add",
          "ads.negative.add"
        ]
      },
      "Diff": {
        "type": "object",
        "properties": {
          "target": {
            "type": "object",
            "additionalProperties": true,
            "description": "What changes: app, locale, review (rating, text), campaign / ad group …"
          },
          "changes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "before": {},
                "after": {}
              }
            }
          },
          "summary": {
            "type": "string"
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Guardrail": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "passed",
              "flagged",
              "unavailable",
              "not_applicable"
            ]
          },
          "score": {
            "type": "number",
            "description": "Lowest AI check probability (1 when none apply). Auto-apply rules compare this."
          },
          "checks": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Probability each holds: policy (store rules), tone (professional), claims (no risky promises), relevance (replies)."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "checks_failed": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "block",
                    "warn"
                  ]
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "model": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Action": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "act_Qm3xXk2Lw9PaZr0cVb"
          },
          "type": {
            "$ref": "#/components/schemas/ActionType"
          },
          "connection_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending_approval",
              "approved",
              "applied",
              "failed",
              "rejected"
            ]
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          },
          "diff": {
            "$ref": "#/components/schemas/Diff"
          },
          "guardrail": {
            "$ref": "#/components/schemas/Guardrail"
          },
          "result": {
            "description": "What the provider returned when applied."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_by": {
            "type": "string",
            "description": "api, mcp, playbook:<name> or dashboard."
          },
          "approved_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "user:<id>, auto (rule) or api."
          },
          "rejected_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "credits": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "applied_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "approve_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Dashboard page where a person approves it."
          },
          "audit": {
            "type": "array",
            "description": "Only on GET /actions/{id}.",
            "items": {
              "type": "object",
              "properties": {
                "event": {
                  "type": "string"
                },
                "actor": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "detail": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "additionalProperties": true
                }
              }
            }
          }
        }
      },
      "ProviderReview": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The provider's review id (use in review.reply)."
          },
          "rating": {
            "type": [
              "integer",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "response": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "body": {
                "type": "string"
              },
              "updated_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "state": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "ASC: PENDING_PUBLISH or PUBLISHED."
              }
            }
          }
        }
      },
      "Listing": {
        "type": "object",
        "properties": {
          "app": {
            "type": "string"
          },
          "locale": {
            "type": "string"
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "editable": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "version_state": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Store": {
        "type": "string",
        "enum": [
          "apple",
          "play"
        ]
      },
      "Freshness": {
        "type": "object",
        "properties": {
          "fetched_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the oldest requested country was last fetched from the store."
          },
          "age_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "stale": {
            "type": "boolean",
            "description": "Older than max_age (or missing)."
          },
          "refreshing": {
            "type": "boolean",
            "description": "A store fetch is still running; poll job_id."
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Meta": {
        "type": "object",
        "properties": {
          "credits_used": {
            "type": "integer"
          },
          "credits_remaining": {
            "type": "integer",
            "description": "Added by the gateway."
          },
          "request_id": {
            "type": "string",
            "description": "Added by the gateway."
          },
          "freshness": {
            "$ref": "#/components/schemas/Freshness"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Things to know (e.g. \"resolved by name\"); safe to show to the user."
          }
        }
      },
      "Envelope": {
        "type": "object",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {},
          "meta": {
            "$ref": "#/components/schemas/Meta"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_param",
                  "invalid_credentials",
                  "unauthorized",
                  "forbidden",
                  "insufficient_credits",
                  "plan_limit",
                  "free_audit_used",
                  "not_found",
                  "app_not_found",
                  "developer_not_found",
                  "job_not_found",
                  "conflict",
                  "not_ready",
                  "rate_limited",
                  "provider_error",
                  "internal_error",
                  "not_configured"
                ]
              },
              "message": {
                "type": "string"
              },
              "hint": {
                "type": "string",
                "description": "What to do next."
              },
              "docs": {
                "type": "string",
                "format": "uri"
              },
              "param": {
                "type": "string"
              }
            }
          }
        }
      },
      "AppCard": {
        "type": "object",
        "properties": {
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "app_id": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "developer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "icon_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "rating": {
            "type": [
              "number",
              "null"
            ]
          },
          "ratings_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "installs_min": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Play install bucket lower bound."
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "Market": {
        "type": "object",
        "properties": {
          "country": {
            "type": "string"
          },
          "available": {
            "type": "boolean"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "rating": {
            "type": [
              "number",
              "null"
            ]
          },
          "ratings_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "App": {
        "type": "object",
        "properties": {
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "app_id": {
            "type": "string"
          },
          "bundle_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "subtitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "App Store subtitle / Play short description."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "developer": {
            "type": "object",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "content_rating": {
            "type": [
              "string",
              "null"
            ]
          },
          "free": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_iap": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "has_ads": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "rating": {
            "type": [
              "number",
              "null"
            ]
          },
          "ratings_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "reviews_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "histogram": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer"
            },
            "description": "1★…5★ counts."
          },
          "installs_min": {
            "type": [
              "integer",
              "null"
            ]
          },
          "installs_exact": {
            "type": [
              "integer",
              "null"
            ]
          },
          "installs_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "release_notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "min_os": {
            "type": [
              "string",
              "null"
            ]
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "released_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "icon_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "screenshots": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "delisted",
              "error"
            ]
          },
          "first_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_fetched_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "markets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Market"
            },
            "description": "The requested countries (all with include=markets)."
          },
          "linked": {
            "type": [
              "object",
              "null"
            ],
            "description": "The same app in the other store.",
            "properties": {
              "store": {
                "$ref": "#/components/schemas/Store"
              },
              "app_id": {
                "type": "string"
              },
              "title": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "icon_url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "match": {
                "type": "string",
                "enum": [
                  "bundle_id",
                  "developer"
                ]
              }
            }
          },
          "iap": {
            "type": "object",
            "description": "include=iap: items (App Store) or price_range (Play)."
          },
          "versions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "version": {
                  "type": "string"
                },
                "first_seen_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "release_notes": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "privacy": {
            "type": "object",
            "description": "include=privacy: App Store privacy labels."
          },
          "estimates": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "country": {
                  "type": "string"
                },
                "month": {
                  "type": "string"
                },
                "downloads": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "revenue_usd": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "confidence": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              }
            }
          },
          "charts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "country": {
                  "type": "string"
                },
                "chart": {
                  "type": "string"
                },
                "category": {
                  "type": "string"
                },
                "date": {
                  "type": "string"
                },
                "rank": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "Review": {
        "type": "object",
        "properties": {
          "review_id": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "rating": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "thumbs_up": {
            "type": [
              "integer",
              "null"
            ]
          },
          "reply": {
            "type": [
              "string",
              "null"
            ],
            "description": "The developer's reply."
          },
          "replied_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sentiment": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "positive",
              "neutral",
              "negative",
              null
            ],
            "description": "AI tag; null = not tagged yet."
          },
          "intent": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "bug",
              "crash",
              "feature_request",
              "pricing",
              "ux",
              "praise",
              "spam",
              "other",
              null
            ],
            "description": "AI tag."
          },
          "severity": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 3,
            "description": "0 none, 1 minor, 2 major, 3 critical."
          },
          "topic": {
            "type": [
              "string",
              "null"
            ],
            "description": "AI topic label (one per review, per-app taxonomy)."
          },
          "topic_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Probability of the chosen intent (0–1)."
          }
        }
      },
      "HistoryPoint": {
        "type": "object",
        "properties": {
          "country": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "rating": {
            "type": [
              "number",
              "null"
            ]
          },
          "ratings_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "reviews_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "installs_min": {
            "type": [
              "integer",
              "null"
            ]
          },
          "installs_exact": {
            "type": [
              "integer",
              "null"
            ]
          },
          "histogram": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer"
            }
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "Change": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "field": {
            "type": "string",
            "example": "version"
          },
          "old": {
            "description": "Previous value (null = absent)."
          },
          "new": {
            "description": "New value (null = removed)."
          },
          "detected_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AppKeyword": {
        "type": "object",
        "properties": {
          "term": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "rank": {
            "type": "integer"
          },
          "popularity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "popularity_source": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "apple",
              "proxy",
              null
            ]
          },
          "difficulty": {
            "type": [
              "integer",
              "null"
            ]
          },
          "results_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ranked_at": {
            "type": "string",
            "format": "date-time"
          },
          "source": {
            "type": "string",
            "enum": [
              "universe",
              "tracked"
            ]
          },
          "tracked_keyword_id": {
            "type": "integer"
          }
        }
      },
      "Competitor": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AppCard"
          },
          {
            "type": "object",
            "properties": {
              "shared_keywords": {
                "type": "integer"
              },
              "best_shared_rank": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "similar_position": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Position in the store's similar-apps list."
              },
              "score": {
                "type": "number"
              }
            }
          }
        ]
      },
      "Chart": {
        "type": "object",
        "properties": {
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "country": {
            "type": "string"
          },
          "chart": {
            "type": "string",
            "enum": [
              "free",
              "paid",
              "grossing"
            ]
          },
          "category": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "previous_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "entries": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/AppCard"
                },
                {
                  "type": "object",
                  "properties": {
                    "rank": {
                      "type": "integer"
                    },
                    "delta": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "is_new": {
                      "type": "boolean"
                    }
                  }
                }
              ]
            }
          },
          "dropped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppCard"
            }
          }
        }
      },
      "KeywordMetrics": {
        "type": "object",
        "properties": {
          "term": {
            "type": "string"
          },
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "country": {
            "type": "string"
          },
          "popularity": {
            "type": [
              "integer",
              "null"
            ],
            "description": "5–100."
          },
          "popularity_source": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "apple",
              "proxy",
              null
            ]
          },
          "difficulty": {
            "type": [
              "integer",
              "null"
            ],
            "description": "0–100."
          },
          "difficulty_breakdown": {
            "type": [
              "object",
              "null"
            ]
          },
          "results_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "est_downloads_at_1": {
            "type": [
              "integer",
              "null"
            ]
          },
          "measured_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "pending": {
            "type": "boolean",
            "description": "Being measured; call again in a minute."
          }
        }
      },
      "Keyword": {
        "allOf": [
          {
            "$ref": "#/components/schemas/KeywordMetrics"
          },
          {
            "type": "object",
            "properties": {
              "top_app_ids": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "top_apps": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AppCard"
                }
              },
              "related": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/KeywordMetrics"
                }
              },
              "suggestions": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "app": {
                "type": "object",
                "properties": {
                  "store": {
                    "$ref": "#/components/schemas/Store"
                  },
                  "app_id": {
                    "type": "string"
                  },
                  "rank": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "relevance": {
                    "type": "number",
                    "description": "0–1: term coverage in title (1), subtitle (0.7), description (0.4)."
                  }
                }
              }
            }
          }
        ]
      },
      "TrackedKeyword": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "term": {
            "type": "string"
          },
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "country": {
            "type": "string"
          },
          "depth": {
            "type": "integer"
          },
          "cadence_minutes": {
            "type": "integer"
          },
          "tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "popularity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "top10": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "ranked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Developer": {
        "type": "object",
        "properties": {
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "developer_id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "privacy_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "app_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "total_ratings": {
            "type": [
              "integer",
              "null"
            ]
          },
          "total_installs": {
            "type": [
              "integer",
              "null"
            ]
          },
          "top_category": {
            "type": [
              "string",
              "null"
            ]
          },
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppCard"
            }
          },
          "linked": {
            "type": "array",
            "description": "The same company in the other store.",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "Finding": {
        "type": "object",
        "description": "A deterministic check over the measured facts.",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "example": "striking_distance"
          },
          "section": {
            "type": "string",
            "enum": [
              "metadata",
              "keywords",
              "reviews",
              "ratings",
              "localization",
              "competitors",
              "creative",
              "maintenance"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "high",
              "medium",
              "low"
            ]
          },
          "title": {
            "type": "string",
            "example": "#14 for \"habit tracker adhd\" (not in the title or subtitle)"
          },
          "evidence": {
            "type": "object",
            "additionalProperties": true,
            "description": "The numbers the finding rests on."
          },
          "fix": {
            "type": "string"
          },
          "playbook": {
            "type": [
              "string",
              "null"
            ],
            "description": "Playbook that automates the fix: https://appgoat.ai/playbooks/{playbook}"
          }
        }
      },
      "AuditReport": {
        "type": "object",
        "properties": {
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "headline": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "sections": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "section": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "body": {
                  "type": "string"
                },
                "finding_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "actions": {
            "type": "array",
            "description": "Prioritised, most impactful first.",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "why": {
                  "type": "string"
                },
                "how": {
                  "type": "string"
                },
                "impact": {
                  "type": "string",
                  "enum": [
                    "high",
                    "medium",
                    "low"
                  ]
                },
                "effort": {
                  "type": "string",
                  "enum": [
                    "low",
                    "medium",
                    "high"
                  ]
                },
                "finding_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "playbook": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "metadata": {
            "type": "array",
            "description": "Listing rewrites within the store character limits.",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "enum": [
                    "title",
                    "subtitle",
                    "short_description",
                    "keywords",
                    "description_first_line"
                  ]
                },
                "current": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "suggested": {
                  "type": "string"
                },
                "chars": {
                  "type": "integer"
                },
                "limit": {
                  "type": "integer"
                },
                "rationale": {
                  "type": "string"
                }
              }
            }
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Finding"
            }
          },
          "narrative": {
            "type": "string",
            "enum": [
              "opus",
              "deterministic"
            ],
            "description": "deterministic = findings only (the AI step was unavailable)."
          },
          "model": {
            "type": [
              "string",
              "null"
            ]
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AuditSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "batched",
              "done",
              "failed"
            ]
          },
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "app_id": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "full",
              "lite"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "icon_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "score": {
            "type": [
              "integer",
              "null"
            ]
          },
          "white_label": {
            "type": "boolean",
            "description": "The report page carries your branding."
          },
          "brand_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ReportBranding": {
        "type": "object",
        "properties": {
          "brand_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "logo_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "accent_color": {
            "type": [
              "string",
              "null"
            ],
            "example": "#1f6feb"
          },
          "cta_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "cta_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "footer": {
            "type": [
              "string",
              "null"
            ]
          },
          "powered_by": {
            "type": "boolean",
            "description": "Shows \"Powered by AppGoat\"."
          }
        }
      },
      "Audit": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "aud_3fK9aQ2mXc81LpRt"
          },
          "slug": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Shareable report page (not indexed by search engines)."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "batched",
              "done",
              "failed"
            ]
          },
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "app_id": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "full",
              "lite"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "icon_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "score": {
            "type": [
              "integer",
              "null"
            ]
          },
          "white_label": {
            "type": "boolean",
            "description": "The report page carries your branding."
          },
          "branding": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ReportBranding"
              },
              {
                "type": "null"
              }
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "report": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AuditReport"
              },
              {
                "type": "null"
              }
            ]
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Finding"
            },
            "description": "Available as soon as the facts are gathered."
          },
          "facts": {
            "type": "object",
            "description": "Only with include=facts: listing, ratings, reviews, keywords (probe), competitors, localization, changes, gaps."
          },
          "credits_charged": {
            "type": "integer"
          },
          "free": {
            "type": "boolean",
            "description": "This was the organization's free first audit."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Insights": {
        "type": "object",
        "properties": {
          "window": {
            "type": "object",
            "properties": {
              "since": {
                "type": "string",
                "format": "date-time"
              },
              "until": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "reviews": {
            "type": "integer"
          },
          "tagged": {
            "type": "integer",
            "description": "Reviews with AI tags (the breakdowns below cover these)."
          },
          "avg_rating": {
            "type": [
              "number",
              "null"
            ]
          },
          "sentiment": {
            "type": "object",
            "properties": {
              "positive": {
                "type": "integer"
              },
              "neutral": {
                "type": "integer"
              },
              "negative": {
                "type": "integer"
              }
            }
          },
          "intents": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "topics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "reviews": {
                  "type": "integer"
                },
                "negative": {
                  "type": "integer"
                },
                "avg_rating": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              }
            }
          },
          "top_issues": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "topic": {
                  "type": "string"
                },
                "reviews": {
                  "type": "integer"
                },
                "share": {
                  "type": "number"
                },
                "avg_severity": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "0 none - 3 critical."
                },
                "intents": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer"
                  }
                },
                "last_30d": {
                  "type": "integer"
                },
                "prev_30d": {
                  "type": "integer"
                },
                "versions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "samples": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "rating": {
                        "type": [
                          "integer",
                          "null"
                        ]
                      },
                      "version": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "created_at": {
                        "type": "string"
                      },
                      "text": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "versions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "version": {
                  "type": "string"
                },
                "first_review_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "reviews": {
                  "type": "integer"
                },
                "avg_rating": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "sentiment": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer"
                  }
                },
                "intents": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer"
                  }
                },
                "topics": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          },
          "sentiment_over_time": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "week": {
                  "type": "string",
                  "format": "date"
                },
                "reviews": {
                  "type": "integer"
                },
                "positive": {
                  "type": "integer"
                },
                "neutral": {
                  "type": "integer"
                },
                "negative": {
                  "type": "integer"
                },
                "avg_rating": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "fetch_123"
          },
          "kind": {
            "type": "string",
            "enum": [
              "fetch"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "partial",
              "failed"
            ]
          },
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "app_id": {
            "type": "string"
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "progress": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "country → ok | unavailable | cached | error: …"
          },
          "done": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WatchKind": {
        "type": "string",
        "enum": [
          "app",
          "keyword",
          "competitor_set"
        ]
      },
      "EventType": {
        "type": "string",
        "enum": [
          "new_version",
          "metadata_change",
          "price_change",
          "featured",
          "in_app_event",
          "installs_milestone",
          "rating_drop",
          "ratings_spike",
          "review_spike",
          "negative_review_spike",
          "chart_enter",
          "chart_leave",
          "rank_change",
          "keyword_entrant"
        ],
        "description": "App / competitor_set watches: `new_version` (version + release notes), `metadata_change` (title, subtitle,\ndescription, screenshots, icon, category…; `payload.changes`), `price_change` (price or in-app purchases),\n`featured` (Editors' Choice, featuring, badges), `in_app_event`, `installs_milestone` (Play install bucket),\n`rating_drop` (7-day drop ≥ settings.rating_drop), `ratings_spike` / `review_spike` (day count ≥\nsettings.spike_factor × the 14-day average), `negative_review_spike` (negative reviews per topic),\n`chart_enter` / `chart_leave` (crossing the top settings.chart_top_n). Keyword watches: `rank_change` (the\nwatch's app moved ≥ settings.rank_move, or entered / left the ranking), `keyword_entrant` (new apps in the\ntop settings.keyword_top_n).\n"
      },
      "WatchSettings": {
        "type": "object",
        "properties": {
          "significance": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 0.5,
            "description": "Minimum significance for an event: Jev's probability that an analyst would want the alert (AI on), or a fixed per-type score."
          },
          "rank_move": {
            "type": "integer",
            "default": 5
          },
          "keyword_top_n": {
            "type": "integer",
            "default": 10
          },
          "chart_top_n": {
            "type": "integer",
            "default": 100
          },
          "rating_drop": {
            "type": "number",
            "default": 0.1,
            "description": "Stars over 7 days."
          },
          "spike_factor": {
            "type": "number",
            "default": 3
          },
          "email_digest": {
            "type": "boolean",
            "default": true,
            "description": "Include in the daily email digest."
          },
          "depth": {
            "type": "integer",
            "description": "Keyword watches: results kept per ranking (Apple ≤ 250, Play ≤ 30)."
          },
          "cadence_minutes": {
            "type": "integer",
            "description": "Keyword watches: minutes between re-ranks (360…10080)."
          }
        }
      },
      "WatchInput": {
        "type": "object",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "$ref": "#/components/schemas/WatchKind"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "app": {
            "type": "string",
            "description": "Any `{app}` input. app: required; competitor_set: your own app (optional); keyword: the app whose ranks to report (optional)."
          },
          "competitors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 50,
            "description": "competitor_set only."
          },
          "terms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 500,
            "description": "keyword only (or `term`)."
          },
          "term": {
            "type": "string"
          },
          "store": {
            "type": "string",
            "enum": [
              "apple",
              "play",
              "both"
            ],
            "description": "keyword watches without app (default both); also disambiguates app inputs."
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "default": [
              "us"
            ]
          },
          "event_types": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/EventType"
            },
            "description": "null = every type of the kind."
          },
          "settings": {
            "$ref": "#/components/schemas/WatchSettings"
          },
          "active": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "WatchPatch": {
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "app": {
            "type": [
              "string",
              "null"
            ]
          },
          "competitors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "terms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "store": {
            "type": "string",
            "enum": [
              "apple",
              "play",
              "both"
            ]
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "event_types": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/EventType"
            }
          },
          "settings": {
            "$ref": "#/components/schemas/WatchSettings"
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "WatchApp": {
        "type": "object",
        "properties": {
          "store": {
            "$ref": "#/components/schemas/Store"
          },
          "app_id": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "icon_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "developer_name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Watch": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "kind": {
            "$ref": "#/components/schemas/WatchKind"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "store": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "apple",
              "play",
              "both",
              null
            ]
          },
          "app": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/WatchApp"
              },
              {
                "type": "null"
              }
            ]
          },
          "competitors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WatchApp"
            },
            "description": "competitor_set only."
          },
          "terms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "keyword only."
          },
          "keywords": {
            "type": "array",
            "description": "keyword only: one slot per store × country × term (id = the tracked-keyword id).",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "term": {
                  "type": "string"
                },
                "store": {
                  "$ref": "#/components/schemas/Store"
                },
                "country": {
                  "type": "string"
                },
                "ranked_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "event_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventType"
            }
          },
          "settings": {
            "$ref": "#/components/schemas/WatchSettings"
          },
          "active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Event": {
        "type": "object",
        "description": "Also the body of webhook deliveries and the GitHub `client_payload` (10 top-level keys).",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/EventType"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "watch_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null once the watch is deleted."
          },
          "app": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "store": {
                    "$ref": "#/components/schemas/Store"
                  },
                  "app_id": {
                    "type": "string"
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "icon_url": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string",
            "description": "Deterministic one-liner, e.g. \"Spotify released version 9.0.2 (was 9.0.1)\"."
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "AI one-line summary (null when AI is off or for low-significance events)."
          },
          "significance": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "payload": {
            "type": "object",
            "description": "Type-specific: version / release_notes, changes[{field, old, new}], rating / previous_rating / drop, keyword / rank / previous_rank, chart / category / rank, topic / sample…"
          }
        },
        "example": {
          "id": "4812",
          "type": "new_version",
          "created_at": "2026-10-22T08:15:02.000Z",
          "watch_id": 12,
          "app": {
            "store": "apple",
            "app_id": "324684580",
            "title": "Spotify",
            "icon_url": null
          },
          "country": "us",
          "title": "Spotify released version 9.0.2 (was 9.0.1)",
          "summary": "Spotify 9.0.2 adds offline lyrics and fixes CarPlay crashes.",
          "significance": 0.81,
          "payload": {
            "version": "9.0.2",
            "previous_version": "9.0.1",
            "release_notes": "Offline lyrics. CarPlay fixes."
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "webhook",
              "slack",
              "github"
            ]
          },
          "url": {
            "type": "string",
            "description": "github: owner/repo; slack: shortened (the URL is a credential)."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "event_types": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/EventType"
            }
          },
          "active": {
            "type": "boolean"
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "failure_count": {
            "type": "integer",
            "description": "Consecutive failed attempts."
          },
          "last_success_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_failure_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "secret": {
            "type": "string",
            "description": "kind webhook: the signing secret, only in the create / rotate response."
          },
          "secret_hint": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "endpoint_id": {
            "type": "integer"
          },
          "event_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "null for test sends."
          },
          "event_type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "response_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "response_body": {
            "type": [
              "string",
              "null"
            ],
            "description": "First 1000 characters."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "duration_ms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      }
    }
  }
}
