{
  "openapi": "3.1.0",
  "info": {
    "title": "Tawak API",
    "version": "1.0.0",
    "summary": "Public read-only JSON API for the Tawak app: metadata, pricing, features, and documentation.",
    "description": "Tawak is a free, offline-first Islamic app for Quran reading, prayer times, Qibla direction, and daily remembrance.\n\nThis API exposes the facts Tawak publishes about itself, in machine-readable form.\n\n## Authentication\n\nNone required. Every endpoint is public, read-only, and returns the same data to everyone, so there are no API keys to issue, no OAuth flow, and no rate-limit identity. Requests that need no credentials are simply sent without an `Authorization` header.\n\n## Versioning and deprecation\n\nWithin `v1` only additive changes are made: new endpoints, new optional parameters, and new response fields. Breaking changes ship as a new major path (`/api/v2`), and `v1` keeps working for at least six months after that. Deprecated endpoints will send `Deprecation` and `Sunset` response headers so a client can act before removal.\n\n## Sandbox\n\nNot applicable by construction. This API has no write surface, so there is nothing to damage: the live endpoints are safe to call freely while developing. No idempotency keys are required because there are no write operations to retry.\n\n## Errors\n\nEvery response, including every error, is JSON. Errors share one shape and carry a stable machine `code`, a human `message`, and a `hint` describing what to do next.",
    "contact": {
      "name": "Tawak",
      "url": "https://www.tawak.app/contact",
      "email": "tawakapp@gmail.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://www.tawak.app/privacy"
    }
  },
  "servers": [
    {
      "url": "https://www.tawak.app",
      "description": "Production. Public, read-only, no authentication."
    }
  ],
  "tags": [
    { "name": "Service", "description": "Describe the API itself." },
    { "name": "App", "description": "Facts about the Tawak app." },
    { "name": "Documentation", "description": "Tawak's published documents, as data." },
    { "name": "Discovery", "description": "Plain documents that agents read directly." }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getServiceDescriptor",
        "summary": "Describe this API",
        "description": "Returns the API surface, the authentication posture, the versioning policy, the error format, and every endpoint with its response schema. Read this first.",
        "tags": ["Service"],
        "responses": {
          "200": {
            "description": "The service descriptor.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ServiceDescriptor" }
              }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Liveness probe",
        "description": "Reports whether the API is serving. Use it to distinguish a broken endpoint from a broken network before retrying.",
        "tags": ["Service"],
        "responses": {
          "200": {
            "description": "The service is up.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Health" } }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/app": {
      "get": {
        "operationId": "getApp",
        "summary": "Get app metadata",
        "description": "Identifying metadata for the Tawak app: display name, bundle identifier, supported platforms, price, and the official store and documentation URLs.",
        "tags": ["App"],
        "responses": {
          "200": {
            "description": "App metadata.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/App" } }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/pricing": {
      "get": {
        "operationId": "getPricing",
        "summary": "Get pricing",
        "description": "Machine-readable pricing. Tawak is free: no subscription, no in-app purchase, no advertising, and no account required. Donations are optional and unlock nothing.",
        "tags": ["App"],
        "responses": {
          "200": {
            "description": "Pricing.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Pricing" } }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/features": {
      "get": {
        "operationId": "listFeatures",
        "summary": "List app features",
        "description": "What Tawak does, grouped by area. Use the category parameter to narrow the response to one area.",
        "tags": ["App"],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Return only features in this category. Omit to return every feature.",
            "schema": {
              "type": "string",
              "enum": ["Quran", "Prayer", "Widgets", "Habit", "Privacy"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matching features, plus the full list of valid categories.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/FeatureCollection" } }
            }
          },
          "400": {
            "description": "The requested category does not exist.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/docs": {
      "get": {
        "operationId": "listDocs",
        "summary": "List documents",
        "description": "Metadata for every document Tawak publishes for agents. Bodies are not inlined; read one with getDoc, or fetch its canonical URL. Cursor-paginated.",
        "tags": ["Documentation"],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Omit for the default of 20.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a previous response's next_cursor. Do not parse it; pass it back unchanged.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of document summaries.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/DocList" } }
            }
          },
          "400": {
            "description": "limit or cursor was invalid.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/docs/{slug}": {
      "get": {
        "operationId": "getDoc",
        "summary": "Read a document",
        "description": "Read one published document in full, as JSON, plus the canonical URL that serves the same text as plain markdown. Use listDocs to discover the slugs.",
        "tags": ["Documentation"],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Document slug, for example \"llms\", \"pricing\", or \"skill-app-guide\".",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "The document.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Doc" } }
            }
          },
          "404": {
            "description": "No document has that slug.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpec",
        "summary": "Get this specification",
        "description": "The OpenAPI document you are reading.",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "This specification.",
            "content": {
              "application/json": { "schema": { "type": "object", "description": "An OpenAPI 3.1.0 document." } }
            }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsTxt",
        "summary": "Get the llms.txt index",
        "description": "The conventional short index for language models, as plain text.",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "The index.",
            "content": { "text/plain": { "schema": { "type": "string" } } }
          }
        }
      }
    },
    "/llms.md": {
      "get": {
        "operationId": "getLlmsMd",
        "summary": "Get the agent overview",
        "description": "Tawak's full agent overview in markdown: capabilities, platforms, and what the app deliberately does not have.",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "The overview.",
            "content": { "text/markdown": { "schema": { "type": "string" } } }
          }
        }
      }
    },
    "/pricing.md": {
      "get": {
        "operationId": "getPricingDoc",
        "summary": "Get the pricing document",
        "description": "Pricing as human-readable markdown. Tawak is free.",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "The pricing document.",
            "content": { "text/markdown": { "schema": { "type": "string" } } }
          }
        }
      }
    },
    "/auth.md": {
      "get": {
        "operationId": "getAuthDoc",
        "summary": "Get the authentication posture",
        "description": "Why Tawak has no credential flow, and why no OAuth discovery documents are published.",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "The authentication document.",
            "content": { "text/markdown": { "schema": { "type": "string" } } }
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "Error": {
        "description": "A structured JSON error. Never an HTML error page.",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["status", "code", "message", "hint", "documentation"],
            "properties": {
              "status": { "type": "integer", "description": "The HTTP status code, repeated in the body so it survives a logging layer." },
              "code": {
                "type": "string",
                "description": "Stable machine-readable code. Branch on this.",
                "enum": ["invalid_parameter", "invalid_cursor", "not_found", "method_not_allowed", "internal_error"]
              },
              "message": { "type": "string", "description": "What went wrong, in one sentence." },
              "hint": { "type": "string", "description": "What to do next to recover." },
              "documentation": { "type": "string", "format": "uri", "description": "Where the human docs live." }
            }
          }
        }
      },
      "EndpointSummary": {
        "type": "object",
        "required": ["path", "method", "operation_id", "description"],
        "properties": {
          "path": { "type": "string" },
          "method": { "type": "string", "enum": ["GET"] },
          "operation_id": { "type": "string" },
          "description": { "type": "string" },
          "response_schema": { "type": "string" },
          "parameters": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ParameterSummary" }
          }
        }
      },
      "ParameterSummary": {
        "type": "object",
        "required": ["name", "in", "required", "type"],
        "properties": {
          "name": { "type": "string" },
          "in": { "type": "string", "enum": ["query", "path"] },
          "required": { "type": "boolean" },
          "type": { "type": "string" },
          "description": { "type": "string" },
          "enum": { "type": "array", "items": { "type": "string" } }
        }
      },
      "ServiceDescriptor": {
        "type": "object",
        "required": ["name", "version", "description", "base_url", "openapi", "documentation", "authentication", "sandbox", "versioning_policy", "endpoints"],
        "properties": {
          "name": { "type": "string" },
          "version": { "type": "string" },
          "description": { "type": "string" },
          "base_url": { "type": "string", "format": "uri" },
          "openapi": { "type": "string", "format": "uri" },
          "documentation": { "type": "string", "format": "uri" },
          "authentication": { "type": "string", "description": "Why no credentials are required." },
          "sandbox": { "type": "string", "description": "Why a separate sandbox is not needed." },
          "versioning_policy": { "type": "string" },
          "error_format": { "type": "object", "additionalProperties": true },
          "endpoints": { "type": "array", "items": { "$ref": "#/components/schemas/EndpointSummary" } }
        }
      },
      "Health": {
        "type": "object",
        "required": ["status", "service", "version"],
        "properties": {
          "status": { "type": "string", "enum": ["ok"] },
          "service": { "type": "string" },
          "version": { "type": "string" }
        }
      },
      "App": {
        "type": "object",
        "required": ["name", "bundleIdentifier", "platforms", "price", "website", "appStore", "googlePlay", "documentation"],
        "properties": {
          "name": { "type": "string" },
          "bundleIdentifier": { "type": "string", "description": "Shared by the iOS and Android builds." },
          "platforms": { "type": "array", "items": { "type": "string" } },
          "price": { "type": "string", "enum": ["free"] },
          "website": { "type": "string", "format": "uri" },
          "appStore": { "type": "string", "format": "uri" },
          "googlePlay": { "type": "string", "format": "uri" },
          "documentation": { "type": "string", "format": "uri" }
        }
      },
      "Pricing": {
        "type": "object",
        "required": ["currency", "price", "model", "subscription", "inAppPurchases", "advertising", "accountRequired", "trial", "paidTierExists", "optionalDonation"],
        "properties": {
          "currency": { "type": "string" },
          "price": { "type": "number", "description": "Always 0. Tawak is free." },
          "model": { "type": "string", "enum": ["free"] },
          "subscription": { "type": "boolean", "const": false },
          "inAppPurchases": { "type": "boolean", "const": false },
          "advertising": { "type": "boolean", "const": false },
          "accountRequired": { "type": "boolean", "const": false },
          "trial": { "type": "boolean", "const": false },
          "paidTierExists": { "type": "boolean", "const": false },
          "optionalDonation": { "type": "boolean" },
          "donationPage": { "type": "string", "format": "uri" },
          "donationUnlocksFeatures": { "type": "boolean", "const": false }
        }
      },
      "Feature": {
        "type": "object",
        "required": ["category", "name", "detail"],
        "properties": {
          "category": { "type": "string", "enum": ["Quran", "Prayer", "Widgets", "Habit", "Privacy"] },
          "name": { "type": "string" },
          "detail": { "type": "string" }
        }
      },
      "FeatureCollection": {
        "type": "object",
        "required": ["data", "count", "categories"],
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Feature" } },
          "count": { "type": "integer" },
          "categories": { "type": "array", "items": { "type": "string" } }
        }
      },
      "DocSummary": {
        "type": "object",
        "required": ["slug", "uri", "path", "title", "description", "mime_type", "characters"],
        "properties": {
          "slug": { "type": "string", "description": "Stable identifier used by getDoc." },
          "uri": { "type": "string", "format": "uri", "description": "The canonical public URL serving the same text." },
          "path": { "type": "string" },
          "title": { "type": "string" },
          "description": { "type": "string" },
          "mime_type": { "type": "string" },
          "characters": { "type": "integer" }
        }
      },
      "Pagination": {
        "type": "object",
        "required": ["limit", "count", "total", "has_more", "next_cursor"],
        "properties": {
          "limit": { "type": "integer" },
          "count": { "type": "integer", "description": "Items in this page." },
          "total": { "type": "integer", "description": "Items across all pages." },
          "has_more": { "type": "boolean" },
          "next_cursor": { "type": ["string", "null"], "description": "Pass as the cursor parameter for the next page. Null on the last page." }
        }
      },
      "DocList": {
        "type": "object",
        "required": ["data", "pagination"],
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/DocSummary" } },
          "pagination": { "$ref": "#/components/schemas/Pagination" }
        }
      },
      "Doc": {
        "type": "object",
        "required": ["slug", "uri", "path", "title", "description", "mime_type", "characters", "content"],
        "properties": {
          "slug": { "type": "string" },
          "uri": { "type": "string", "format": "uri" },
          "path": { "type": "string" },
          "title": { "type": "string" },
          "description": { "type": "string" },
          "mime_type": { "type": "string" },
          "characters": { "type": "integer" },
          "content": { "type": "string", "description": "The document body." }
        }
      }
    }
  }
}
