{
  "openapi": "3.1.0",
  "info": {
    "title": "findasaas API",
    "version": "1.0.0",
    "summary": "Read the findasaas SaaS directory programmatically.",
    "description": "findasaas is a curated directory of SaaS products. This API exposes the\ndirectory itself: search and filter listings, resolve a slug to a full\nproduct record, and enumerate the category vocabulary.\n\nRead endpoints are public and need no credentials, so an agent can call\nthem without an onboarding step. Write access uses a personal API key\nwith explicit scopes, issued self-serve from the dashboard.\n\nEvery error is JSON with a stable `code`, a `message` and a `hint`.",
    "contact": {
      "name": "findasaas",
      "url": "https://findasaas.com/contact",
      "email": "info@9thavenue.dev"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://findasaas.com/privacy"
    }
  },
  "servers": [
    {
      "url": "https://findasaas.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Developer guide",
    "url": "https://findasaas.com/developers"
  },
  "tags": [
    {
      "name": "Listings",
      "description": "SaaS products in the directory."
    },
    {
      "name": "Categories",
      "description": "The category vocabulary used for filtering."
    }
  ],
  "paths": {
    "/api/v1/listings": {
      "get": {
        "operationId": "listListings",
        "tags": [
          "Listings"
        ],
        "summary": "List and search SaaS products",
        "description": "Returns active directory listings, newest and most-upvoted first. Use `search` for free text over name and description, and `category` to restrict to one category slug. Public: no authentication required.",
        "security": [],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Free-text match against listing name and description.",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "examples": {
              "invoicing": {
                "value": "invoicing"
              }
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Restrict to one category slug. Unknown slugs return 404 rather than an unfiltered list.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Results per page (max 100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of listings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Listing"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A query parameter was malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The requested category slug does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/listings/{slug}": {
      "get": {
        "operationId": "getListing",
        "tags": [
          "Listings"
        ],
        "summary": "Get one SaaS product by slug",
        "description": "Resolves a listing slug to the full public record. Only active listings are returned. Public: no authentication required.",
        "security": [],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "The listing slug, as returned by listListings.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The listing.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Listing"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No active listing with that slug.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/categories": {
      "get": {
        "operationId": "listCategories",
        "tags": [
          "Categories"
        ],
        "summary": "List directory categories",
        "description": "Returns every active category with its listing count. Call this before filtering listListings by category. Public: no authentication required.",
        "security": [],
        "responses": {
          "200": {
            "description": "All active categories.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Category"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Listing": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "url",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable opaque identifier."
          },
          "slug": {
            "type": "string",
            "description": "URL-safe identifier, used as the path segment on the site.",
            "examples": [
              "usewok"
            ]
          },
          "name": {
            "type": "string",
            "description": "Product name as the founder wrote it."
          },
          "tagline": {
            "type": [
              "string",
              "null"
            ],
            "description": "First sentence of the description, capped at 160 characters."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Full product description."
          },
          "website": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Product homepage."
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Category slugs this listing belongs to."
          },
          "logo": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "coverImage": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "isVerified": {
            "type": "boolean",
            "description": "Ownership confirmed by the founder."
          },
          "isFeatured": {
            "type": "boolean",
            "description": "Currently boosted in the directory."
          },
          "upvotes": {
            "type": "integer",
            "minimum": 0
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical page on findasaas."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Category": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "listingCount",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Value to pass as the `category` filter."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "listingCount": {
            "type": "integer",
            "minimum": 0
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "required": [
          "total"
        ],
        "properties": {
          "page": {
            "type": "integer",
            "minimum": 1
          },
          "perPage": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "total": {
            "type": "integer",
            "minimum": 0
          },
          "totalPages": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "hint",
              "documentation"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "method_not_allowed",
                  "not_acceptable",
                  "rate_limited",
                  "internal_error"
                ],
                "description": "Stable machine-readable code. Branch on this, not the message."
              },
              "message": {
                "type": "string",
                "description": "Human-readable summary of the failure."
              },
              "hint": {
                "type": "string",
                "description": "What to change to make the call succeed."
              },
              "requiredScopes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "listings:read",
                    "listings:write",
                    "posts:read",
                    "posts:write"
                  ]
                },
                "description": "Present on 401/403: scopes this operation needs."
              },
              "documentation": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fas_<hex>",
        "description": "Personal API key sent as `Authorization: Bearer fas_...`. Keys carry explicit scopes; a call outside its scopes returns 403 with the required scope named in `requiredScopes`."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 with PKCE, discoverable at /.well-known/oauth-authorization-server. Used by MCP clients.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://findasaas.com/api/auth/mcp/authorize",
            "tokenUrl": "https://findasaas.com/api/auth/mcp/token",
            "scopes": {
              "listings:read": "Read public directory listings and categories.",
              "listings:write": "Create and update listings you own.",
              "posts:read": "Read product updates on listings you own.",
              "posts:write": "Publish and edit product updates on listings you own."
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "apiKey": []
    },
    {
      "oauth2": [
        "listings:read",
        "listings:write",
        "posts:read",
        "posts:write"
      ]
    }
  ]
}