{
 "openapi": "3.1.0",
 "info": {
  "title": "AI Deprecations API",
  "version": "1.1.0",
  "description": "Read-only static JSON API of AI model deprecation and shutdown dates, normalized from official provider pages. Every record has source_url and last_checked. Data CC BY 4.0; cite AI Deprecations with a link.",
  "license": {
   "name": "CC BY 4.0",
   "url": "https://creativecommons.org/licenses/by/4.0/"
  },
  "contact": {
   "name": "AI Deprecations (sponsorships and API)",
   "email": "modelsunset@mail.grokbot.com",
   "url": "https://aideprecations.neocities.org/sponsor/"
  },
  "x-subscribe": "https://aideprecations.neocities.org/subscribe/",
  "x-media-kit": "https://aideprecations.neocities.org/sponsor/media-kit.json"
 },
 "servers": [
  {
   "url": "https://aideprecations--89f34e4cd29f11f1a4961607ee4eb77e.web.val.run",
   "description": "Counted endpoint (302 redirect to the static site)"
  },
  {
   "url": "https://aideprecations.neocities.org",
   "description": "Direct static site (fallback)"
  }
 ],
 "paths": {
  "/api/v1/models.json": {
   "get": {
    "operationId": "listModels",
    "summary": "All tracked models",
    "responses": {
     "200": {
      "description": "All models",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "models": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Model"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/providers/{provider}.json": {
   "get": {
    "operationId": "getProvider",
    "summary": "Models for one provider",
    "parameters": [
     {
      "name": "provider",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "enum": [
        "openai",
        "anthropic",
        "google-gemini",
        "google-vertex",
        "azure-openai",
        "aws-bedrock",
        "mistral",
        "cohere"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Provider models",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "models": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Model"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/models/{provider}/{slug}.json": {
   "get": {
    "operationId": "getModel",
    "summary": "One model",
    "parameters": [
     {
      "name": "provider",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "slug",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Model",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "model": {
           "$ref": "#/components/schemas/Model"
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/upcoming/{days}.json": {
   "get": {
    "operationId": "listUpcoming",
    "summary": "Shutdowns in the next N days",
    "parameters": [
     {
      "name": "days",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer",
       "enum": [
        7,
        30,
        90,
        365
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Upcoming shutdowns",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "models": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Model"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/changes": {
   "servers": [
    {
     "url": "https://aideprecations--89f34e4cd29f11f1a4961607ee4eb77e.web.val.run",
     "description": "Live subscription hub (not on the static site)"
    }
   ],
   "get": {
    "operationId": "getChangesSince",
    "summary": "Records changed since a time",
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "provider",
      "in": "query",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "since, tracking_since, checked_at, count, changes[], and warning when since is earlier than tracking_since (history before tracking_since is not recorded). Poll with since = previous checked_at. changes[] (each a Model plus changed_at and change{type,fields})"
     }
    }
   }
  },
  "/subscribe": {
   "servers": [
    {
     "url": "https://aideprecations--89f34e4cd29f11f1a4961607ee4eb77e.web.val.run",
     "description": "Live subscription hub (not on the static site)"
    }
   ],
   "post": {
    "operationId": "subscribe",
    "summary": "Subscribe a webhook (JSON POST on changes, checked daily) or an email (double opt-in newsletter)",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "webhook": {
          "type": "string",
          "format": "uri",
          "description": "public https URL"
         },
         "email": {
          "type": "string",
          "format": "email"
         },
         "providers": {
          "type": "array",
          "items": {
           "type": "string",
           "enum": [
            "openai",
            "anthropic",
            "google-gemini",
            "google-vertex",
            "azure-openai",
            "aws-bedrock",
            "mistral",
            "cohere"
           ]
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok, status, unsubscribe_token (webhooks)"
     },
     "400": {
      "description": "invalid input"
     }
    }
   }
  },
  "/unsubscribe": {
   "servers": [
    {
     "url": "https://aideprecations--89f34e4cd29f11f1a4961607ee4eb77e.web.val.run",
     "description": "Live subscription hub (not on the static site)"
    }
   ],
   "post": {
    "operationId": "unsubscribe",
    "summary": "Stop webhook alerts",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "token": {
          "type": "string"
         }
        },
        "required": [
         "token"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok, status"
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Model": {
    "type": "object",
    "properties": {
     "provider": {
      "type": [
       "string",
       "null"
      ]
     },
     "provider_name": {
      "type": [
       "string",
       "null"
      ]
     },
     "model_id": {
      "type": [
       "string",
       "null"
      ]
     },
     "slug": {
      "type": [
       "string",
       "null"
      ]
     },
     "status": {
      "type": [
       "string",
       "null"
      ]
     },
     "announced": {
      "type": [
       "string",
       "null"
      ]
     },
     "shutdown": {
      "type": [
       "string",
       "null"
      ]
     },
     "shutdown_note": {
      "type": [
       "string",
       "null"
      ]
     },
     "replacement": {
      "type": [
       "string",
       "null"
      ],
      "description": "Provider's own replacement wording, verbatim (may list several models joined by ' or ' or commas)."
     },
     "replacement_status": {
      "type": [
       "string",
       "null"
      ],
      "description": "Status of the resolved replacement. Resolution rule: split replacement on ' or ', commas and whitespace; keep tokens that exactly match a model_id in the SAME provider; pick the best status (active, then deprecated, then retired). No cross-provider lookup."
     },
     "replacement_shutdown": {
      "type": [
       "string",
       "null"
      ]
     },
     "replacement_final": {
      "type": [
       "string",
       "null"
      ],
      "description": "End of the replacement chain within the same provider: the first active model reached, or, if none, the deepest tracked model. Null means the pointer leaves our data."
     },
     "replacement_final_status": {
      "type": [
       "string",
       "null"
      ],
      "description": "Status of replacement_final (active / deprecated / retired)."
     },
     "replacement_provider": {
      "type": [
       "string",
       "null"
      ],
      "description": "Provider of the resolved replacement (always the row's own provider)."
     },
     "replacement_final_provider": {
      "type": [
       "string",
       "null"
      ],
      "description": "Provider of replacement_final."
     },
     "replacement_page": {
      "type": [
       "string",
       "null"
      ],
      "description": "page of the resolved replacement row. page is the unique row key in this file (model_id is not unique, even within a provider). Tie-break when several rows match: best status, then the order the provider lists the models in, then (for duplicate model_id rows) no shutdown or the latest shutdown first, then page."
     },
     "replacement_final_page": {
      "type": [
       "string",
       "null"
      ],
      "description": "page of the replacement_final row (unique key; join on this, not on model_id)."
     },
     "source_url": {
      "type": [
       "string",
       "null"
      ]
     },
     "last_checked": {
      "type": [
       "string",
       "null"
      ]
     },
     "last_attempt": {
      "type": [
       "string",
       "null"
      ],
      "description": "When we last tried to read source_url for this row."
     },
     "check_status": {
      "type": [
       "string",
       "null"
      ],
      "description": "verified = last attempt read the row from source_url; not_verified = last attempt failed, row carried forward unchanged (see data.json sources[] for error_class)."
     },
     "page": {
      "type": [
       "string",
       "null"
      ]
     },
     "api": {
      "type": [
       "string",
       "null"
      ]
     },
     "markdown": {
      "type": [
       "string",
       "null"
      ]
     }
    }
   }
  }
 }
}