{
  "openapi": "3.1.0",
  "info": {
    "title": "AdSwipeFile API",
    "version": "1.0.0",
    "summary": "Read the AdSwipeFile ad library from software.",
    "description": "AdSwipeFile is an ad library of 22,543+ winning ads from top brands on Meta (Facebook and Instagram), kept after Meta takes them down and filed by brand, niche and format, for the freelancers, agencies and brands who make ads. Search it, or ask Claude, ChatGPT or another AI agent to pull ads for you. Free to start.\n\nRead-only. /api/v1 needs a free account: an OAuth 2.1 access token (the one the MCP flow issues; scope mcp:read) or a signed-in session cookie, and returns per list the newest 12 ads and no ad copy (search results carry it on Starter and Growth). Each request counts against the account's MCP/API allowance (50 a day on Free; see https://adswipefile.com/pricing). For the whole library use the MCP server (x-mcp) with the same token. Markdown pages need no credentials. Docs: https://adswipefile.com/docs/api.",
    "contact": {
      "name": "AdSwipeFile",
      "email": "hello@adswipefile.com",
      "url": "https://adswipefile.com/contact"
    },
    "termsOfService": "https://adswipefile.com/terms"
  },
  "servers": [
    {
      "url": "https://adswipefile.com"
    }
  ],
  "externalDocs": {
    "url": "https://adswipefile.com/docs/api"
  },
  "tags": [
    {
      "name": "library",
      "description": "The ad library as JSON."
    },
    {
      "name": "pages",
      "description": "Any page as markdown."
    },
    {
      "name": "ops",
      "description": "Service status."
    }
  ],
  "paths": {
    "/api/v1/search": {
      "get": {
        "operationId": "searchLibrary",
        "security": [
          {
            "oauth2": [
              "mcp:read"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "tags": [
          "library"
        ],
        "summary": "Search ads, brands, niches and formats",
        "description": "Keyword search across the library. Start here to turn a product, brand or idea into slugs for listAds, or into ad ids for getAd. Results carry each hit's type and page URL.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Keywords, e.g. \"protein powder\", \"nike\", \"before after\".",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matches, best first.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or out of range.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No credentials, or a missing, invalid or expired token. Get one: see the oauth2 security scheme.",
            "headers": {
              "WWW-Authenticate": {
                "description": "RFC 9728 / RFC 6750 challenge: `Bearer resource_metadata=\"…/.well-known/oauth-protected-resource/api/mcp\", scope=\"mcp:read offline_access\"`. The same one /api/mcp sends.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the mcp:read scope.",
            "headers": {
              "WWW-Authenticate": {
                "description": "RFC 9728 / RFC 6750 challenge: `Bearer resource_metadata=\"…/.well-known/oauth-protected-resource/api/mcp\", scope=\"mcp:read offline_access\"`. The same one /api/mcp sends.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such brand, niche, format or ad.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: more than 60 requests a minute from this account. `quota_exceeded`: the plan's MCP/API allowance is used (50 a day on Free, 10,000 a month on Starter, 50,000 a month on Growth, UTC); `detail` says when it resets and `resolution` what to do next, with a link to the plans page. Relay both to the person.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait: to the next minute's room for rate_limited; for quota_exceeded, to the allowance's reset (UTC): the next midnight on Free, the first of next month on Starter and Growth.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The library could not be read; retry shortly.",
            "headers": {},
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ads": {
      "get": {
        "operationId": "listAds",
        "security": [
          {
            "oauth2": [
              "mcp:read"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "tags": [
          "library"
        ],
        "summary": "The newest ads, optionally by brand, niche and format",
        "description": "The newest ads, newest first, optionally narrowed by brand, niche and format slugs (from searchLibrary or the page URLs). Returns at most 12 (the paywall's page) and has no next page; the MCP tool list_ads pages further.",
        "parameters": [
          {
            "name": "brand",
            "in": "query",
            "required": false,
            "description": "Brand slug, e.g. nike.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,119}$"
            }
          },
          {
            "name": "niche",
            "in": "query",
            "required": false,
            "description": "Niche (industry) slug, e.g. beauty.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,119}$"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Format slug, e.g. ugc.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,119}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many ads, 1 to 12.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 12,
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The ads.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdList"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or out of range.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No credentials, or a missing, invalid or expired token. Get one: see the oauth2 security scheme.",
            "headers": {
              "WWW-Authenticate": {
                "description": "RFC 9728 / RFC 6750 challenge: `Bearer resource_metadata=\"…/.well-known/oauth-protected-resource/api/mcp\", scope=\"mcp:read offline_access\"`. The same one /api/mcp sends.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the mcp:read scope.",
            "headers": {
              "WWW-Authenticate": {
                "description": "RFC 9728 / RFC 6750 challenge: `Bearer resource_metadata=\"…/.well-known/oauth-protected-resource/api/mcp\", scope=\"mcp:read offline_access\"`. The same one /api/mcp sends.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such brand, niche, format or ad.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: more than 60 requests a minute from this account. `quota_exceeded`: the plan's MCP/API allowance is used (50 a day on Free, 10,000 a month on Starter, 50,000 a month on Growth, UTC); `detail` says when it resets and `resolution` what to do next, with a link to the plans page. Relay both to the person.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait: to the next minute's room for rate_limited; for quota_exceeded, to the allowance's reset (UTC): the next midnight on Free, the first of next month on Starter and Growth.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The library could not be read; retry shortly.",
            "headers": {},
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ads/{id}": {
      "get": {
        "operationId": "getAd",
        "security": [
          {
            "oauth2": [
              "mcp:read"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "tags": [
          "library"
        ],
        "summary": "One ad's card",
        "description": "One ad: name, description, brand, niche, formats, image and page URL. Ids come from listAds or searchLibrary.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The ad id, as in /ads/{id}.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The ad.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdResponse"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or out of range.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No credentials, or a missing, invalid or expired token. Get one: see the oauth2 security scheme.",
            "headers": {
              "WWW-Authenticate": {
                "description": "RFC 9728 / RFC 6750 challenge: `Bearer resource_metadata=\"…/.well-known/oauth-protected-resource/api/mcp\", scope=\"mcp:read offline_access\"`. The same one /api/mcp sends.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the mcp:read scope.",
            "headers": {
              "WWW-Authenticate": {
                "description": "RFC 9728 / RFC 6750 challenge: `Bearer resource_metadata=\"…/.well-known/oauth-protected-resource/api/mcp\", scope=\"mcp:read offline_access\"`. The same one /api/mcp sends.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such brand, niche, format or ad.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: more than 60 requests a minute from this account. `quota_exceeded`: the plan's MCP/API allowance is used (50 a day on Free, 10,000 a month on Starter, 50,000 a month on Growth, UTC); `detail` says when it resets and `resolution` what to do next, with a link to the plans page. Relay both to the person.",
            "headers": {
              "RateLimit-Policy": {
                "description": "IETF draft structured field, per account: \"account\";q=60;w=60.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Remaining quota and seconds to reset: \"account\";r=59;t=42.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait: to the next minute's room for rate_limited; for quota_exceeded, to the allowance's reset (UTC): the next midnight on Free, the first of next month on Starter and Growth.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The library could not be read; retry shortly.",
            "headers": {},
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "security": [],
        "tags": [
          "ops"
        ],
        "summary": "Liveness and the running build",
        "description": "Answers 200 while the server runs. `buildId:startedAt` identifies the running deploy.",
        "responses": {
          "200": {
            "description": "Up.",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/{path}": {
      "get": {
        "operationId": "getPageAsMarkdown",
        "security": [],
        "tags": [
          "pages"
        ],
        "summary": "Any page as markdown, at its own URL",
        "description": "Any page of the site as markdown instead of HTML, at the same URL: send Accept: text/markdown. Use it to read a niche, format, brand or ad page without parsing HTML. Lists show the signed-out view. Unknown paths answer 404 in markdown; a client that accepts neither markdown nor HTML gets 406. Enumerate paths from /sitemap.xml.",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "A page path without the leading slash, e.g. niches/beauty.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Accept",
            "in": "header",
            "required": true,
            "description": "text/markdown. q-values are honoured (RFC 9110); a tie with text/html resolves to HTML.",
            "schema": {
              "type": "string",
              "const": "text/markdown"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page as markdown.",
            "headers": {},
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No page at that path; the body says where to look instead.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "406": {
            "description": "Neither text/markdown nor text/html is acceptable.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/md/{path}": {
      "get": {
        "operationId": "getMarkdownRendition",
        "security": [],
        "tags": [
          "pages"
        ],
        "summary": "The same markdown at a URL of its own",
        "description": "For a client that cannot set request headers. Prefer getPageAsMarkdown; this URL is marked noindex.",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "A page path without the leading slash.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page as markdown.",
            "headers": {},
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No page at that path; the body says where to look instead.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "title": "Problem",
        "description": "RFC 9457 problem details. Every JSON error here has this shape: branch on `code`, show `detail`, follow `resolution`.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "const": "https://adswipefile.com/docs/api#errors"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this request."
          },
          "code": {
            "type": "string",
            "enum": [
              "not_found",
              "bad_request",
              "unauthorized",
              "forbidden",
              "method_not_allowed",
              "rate_limited",
              "quota_exceeded",
              "not_acceptable",
              "service_unavailable"
            ],
            "description": "Stable machine token. Closed set."
          },
          "resolution": {
            "type": "string",
            "description": "What to do next."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "resolution"
        ]
      },
      "Ref": {
        "type": "object",
        "title": "Ref",
        "description": "A brand, niche or format: its name, slug and page.",
        "properties": {
          "name": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        },
        "required": [
          "name",
          "slug",
          "url"
        ]
      },
      "AdCard": {
        "type": "object",
        "title": "AdCard",
        "description": "One ad as its page shows it. The ad's copy is not included: read it with the MCP tool get_ad.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "brand": {
            "$ref": "#/components/schemas/Ref"
          },
          "niche": {
            "$ref": "#/components/schemas/Ref"
          },
          "formats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Ref"
            }
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The creative's image."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The ad's page; link it when citing the ad."
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "brand",
          "niche",
          "formats",
          "image",
          "createdAt",
          "url"
        ]
      },
      "AdList": {
        "type": "object",
        "title": "AdList",
        "properties": {
          "ads": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdCard"
            },
            "maxItems": 12
          },
          "count": {
            "type": "integer"
          },
          "filters": {
            "type": "object",
            "properties": {
              "brand": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{0,119}$"
              },
              "niche": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{0,119}$"
              },
              "format": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{0,119}$"
              }
            },
            "additionalProperties": false
          },
          "moreAvailable": {
            "type": "boolean",
            "description": "More ads match than anonymous access returns."
          },
          "fullAccess": {
            "type": "string",
            "description": "How to read the rest."
          }
        },
        "required": [
          "ads",
          "count",
          "filters",
          "moreAvailable",
          "fullAccess"
        ]
      },
      "AdResponse": {
        "type": "object",
        "title": "AdResponse",
        "properties": {
          "ad": {
            "$ref": "#/components/schemas/AdCard"
          },
          "fullAccess": {
            "type": "string"
          }
        },
        "required": [
          "ad",
          "fullAccess"
        ]
      },
      "SearchResponse": {
        "type": "object",
        "title": "SearchResponse",
        "properties": {
          "query": {
            "type": "string",
            "description": "The query as searched: trimmed, lower-cased."
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "ad",
                    "brand",
                    "niche",
                    "format"
                  ]
                },
                "id": {
                  "type": "string"
                },
                "slug": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "For brand, niche and format: the filter value for listAds."
                },
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": "string",
                  "description": "Brand, niche and format: their description. Ad: the ad's copy, on Starter and Growth only; absent on Free."
                },
                "url": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uri"
                }
              },
              "required": [
                "type",
                "id",
                "slug",
                "title",
                "url"
              ]
            }
          }
        },
        "required": [
          "query",
          "results"
        ]
      },
      "Health": {
        "type": "object",
        "title": "Health",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "buildId": {
            "type": "string"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "ok",
          "buildId",
          "startedAt"
        ]
      }
    },
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization code with PKCE (S256), the flow the MCP server uses: one token works for both. Register a client at https://adswipefile.com/api/auth/oauth2/register (dynamic registration is open) and send resource=https://adswipefile.com/api/mcp. Metadata: https://adswipefile.com/.well-known/oauth-authorization-server.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://adswipefile.com/api/auth/oauth2/authorize",
            "tokenUrl": "https://adswipefile.com/api/auth/oauth2/token",
            "refreshUrl": "https://adswipefile.com/api/auth/oauth2/token",
            "scopes": {
              "mcp:read": "Read the ad library and your saved ads",
              "offline_access": "Stay connected"
            }
          }
        }
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Secure-better-auth.session_token",
        "description": "A signed-in session on the site, for first-party browser calls."
      }
    }
  },
  "x-mcp": {
    "url": "https://adswipefile.com/api/mcp",
    "transport": "streamable-http",
    "authorization": "OAuth 2.1, dynamic client registration; a person signs in once (email or Google). Free.",
    "protectedResourceMetadata": "https://adswipefile.com/.well-known/oauth-protected-resource",
    "serverCard": "https://adswipefile.com/.well-known/mcp.json",
    "documentation": "https://adswipefile.com/docs/mcp",
    "tools": [
      "search_library",
      "list_ads",
      "get_ad",
      "list_brands",
      "list_niches",
      "list_formats",
      "list_saved_ads",
      "list_followed_ads"
    ]
  }
}