{
  "openapi": "3.1.0",
  "info": {
    "title": "Annolux Search API",
    "version": "1.0.0",
    "description": "The independent Annolux standard Search, account, OAuth, Playground, and billing API. Paid plans include standard Search only."
  },
  "servers": [{"url": "https://api.annolux.com"}],
  "tags": [
    {"name": "Search"}, {"name": "Health"}, {"name": "OAuth"},
    {"name": "Account"}, {"name": "Billing"}, {"name": "Internal webhook"}
  ],
  "paths": {
    "/api/v1/health": {
      "get": {"tags": ["Health"], "operationId": "health", "responses": {"200": {"description": "Process is healthy", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Health"}}}}}}
    },
    "/api/v1/ready": {
      "get": {"tags": ["Health"], "operationId": "ready", "responses": {"200": {"description": "Account store and search index are ready", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Health"}}}}, "503": {"$ref": "#/components/responses/Error"}}}
    },
    "/api/v1/search": {
      "post": {
        "tags": ["Search"], "operationId": "search", "security": [{"apiKey": []}],
        "description": "Consumes exactly one credit only after a successful 2xx response. Errors and timeouts consume zero.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchRequest"}}}},
        "responses": {
          "200": {"description": "Ranked results", "headers": {"X-Annolux-Credits-Used": {"schema": {"type": "integer", "const": 1}}, "X-Annolux-Credits-Remaining": {"schema": {"type": "integer"}}, "X-Request-ID": {"schema": {"type": "string"}}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchResponse"}}}},
          "400": {"$ref": "#/components/responses/Error"}, "401": {"$ref": "#/components/responses/Error"}, "402": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}, "429": {"$ref": "#/components/responses/Error"}, "500": {"$ref": "#/components/responses/Error"}, "502": {"$ref": "#/components/responses/Error"}, "503": {"$ref": "#/components/responses/Error"}, "504": {"$ref": "#/components/responses/Error"}
        }
      }
    },
    "/api/v1/demo/search": {
      "post": {"tags": ["Search"], "operationId": "demoSearch", "security": [], "description": "Unauthenticated homepage demo of the identical Search contract. No credits move. limit is clamped to 3 and timeout to 10 seconds; per-IP pacing and a global daily quota apply. For real workloads create a free account and use /api/v1/search.", "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchRequest"}}}}, "responses": {"200": {"description": "Ranked results (limit clamped to 3)", "headers": {"X-Request-ID": {"schema": {"type": "string"}}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchResponse"}}}}, "400": {"$ref": "#/components/responses/Error"}, "429": {"$ref": "#/components/responses/Error"}, "500": {"$ref": "#/components/responses/Error"}, "502": {"$ref": "#/components/responses/Error"}, "503": {"$ref": "#/components/responses/Error"}, "504": {"$ref": "#/components/responses/Error"}}}
    },
    "/api/v1/playground/search": {
      "post": {"tags": ["Search"], "operationId": "playgroundSearch", "security": [{"sessionCookie": [], "csrf": []}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchRequest"}}}}, "responses": {"200": {"description": "Ranked results", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchResponse"}}}}, "400": {"$ref": "#/components/responses/Error"}, "401": {"$ref": "#/components/responses/Error"}, "402": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}, "429": {"$ref": "#/components/responses/Error"}}}
    },
    "/api/v1/auth/google/start": {"get": {"tags": ["OAuth"], "operationId": "startGoogleOAuth", "parameters": [{"$ref": "#/components/parameters/ReturnPath"}], "responses": {"302": {"description": "Redirect to Google"}, "503": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/auth/google/callback": {"get": {"tags": ["OAuth"], "operationId": "finishGoogleOAuth", "parameters": [{"name": "state", "in": "query", "required": true, "schema": {"type": "string"}}, {"name": "code", "in": "query", "required": true, "schema": {"type": "string"}}], "responses": {"303": {"description": "Session created and redirected to annolux.com"}}}},
    "/api/v1/auth/github/start": {"get": {"tags": ["OAuth"], "operationId": "startGitHubOAuth", "parameters": [{"$ref": "#/components/parameters/ReturnPath"}], "responses": {"302": {"description": "Redirect to GitHub"}, "503": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/auth/github/callback": {"get": {"tags": ["OAuth"], "operationId": "finishGitHubOAuth", "parameters": [{"name": "state", "in": "query", "required": true, "schema": {"type": "string"}}, {"name": "code", "in": "query", "required": true, "schema": {"type": "string"}}], "responses": {"303": {"description": "Session created and redirected to annolux.com"}}}},
    "/api/v1/auth/logout": {"post": {"tags": ["OAuth"], "operationId": "logout", "security": [{"sessionCookie": [], "csrf": []}], "responses": {"204": {"description": "Session revoked"}, "401": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/account/me": {"get": {"tags": ["Account"], "operationId": "accountMe", "security": [{"sessionCookie": []}], "responses": {"200": {"description": "Account and CSRF token", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Account"}}}}, "401": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/account/usage": {"get": {"tags": ["Account"], "operationId": "accountUsage", "security": [{"sessionCookie": []}], "responses": {"200": {"description": "Credit summary", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreditSummary"}}}}, "401": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/account/api-keys": {
      "get": {
        "tags": ["Account"],
        "operationId": "listAPIKeys",
        "security": [{"sessionCookie": []}],
        "responses": {
          "200": {"description": "API key metadata; never raw keys"},
          "401": {"$ref": "#/components/responses/Error"}
        }
      },
      "post": {
        "tags": ["Account"],
        "operationId": "createAPIKey",
        "security": [{"sessionCookie": [], "csrf": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["name"],
                "properties": {"name": {"type": "string", "minLength": 1, "maxLength": 80}}
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Raw secret shown once",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["api_key", "secret", "shown_once"],
                  "properties": {
                    "api_key": {"$ref": "#/components/schemas/APIKey"},
                    "secret": {"type": "string", "pattern": "^ann_live_"},
                    "shown_once": {"type": "boolean", "const": true}
                  }
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Error"},
          "403": {"$ref": "#/components/responses/Error"},
          "409": {"$ref": "#/components/responses/Error"}
        }
      }
    },
    "/api/v1/account/api-keys/{keyID}": {"delete": {"tags": ["Account"], "operationId": "revokeAPIKey", "security": [{"sessionCookie": [], "csrf": []}], "parameters": [{"name": "keyID", "in": "path", "required": true, "schema": {"type": "string"}}], "responses": {"204": {"description": "Key revoked"}, "401": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/account/identities": {"get": {"tags": ["Account"], "operationId": "listIdentities", "security": [{"sessionCookie": []}], "responses": {"200": {"description": "Linked Google and GitHub identities"}, "401": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/account/identities/google/start": {"post": {"tags": ["OAuth"], "operationId": "linkGoogle", "security": [{"sessionCookie": [], "csrf": []}], "parameters": [{"$ref": "#/components/parameters/ReturnPath"}], "responses": {"200": {"description": "One-time Google authorization URL"}, "401": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/account/identities/github/start": {"post": {"tags": ["OAuth"], "operationId": "linkGitHub", "security": [{"sessionCookie": [], "csrf": []}], "parameters": [{"$ref": "#/components/parameters/ReturnPath"}], "responses": {"200": {"description": "One-time GitHub authorization URL"}, "401": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/billing/checkout-intents": {"post": {"tags": ["Billing"], "operationId": "createCheckoutIntent", "security": [{"sessionCookie": [], "csrf": []}], "description": "Sandbox-ready. Live remains disabled until seller support requirements are approved.", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": false, "required": ["plan", "accept_terms", "accept_refund"], "properties": {"plan": {"type": "string", "enum": ["pro", "scale"]}, "accept_terms": {"type": "boolean", "const": true}, "accept_refund": {"type": "boolean", "const": true}}}}}}, "responses": {"201": {"description": "Random checkout intent, client token, price ID, and custom data"}, "400": {"$ref": "#/components/responses/Error"}, "401": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}, "409": {"$ref": "#/components/responses/Error"}, "503": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/billing/portal-sessions": {"post": {"tags": ["Billing"], "operationId": "createPortalSession", "security": [{"sessionCookie": [], "csrf": []}], "responses": {"201": {"description": "Paddle-hosted portal URL"}, "401": {"$ref": "#/components/responses/Error"}, "403": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}, "503": {"$ref": "#/components/responses/Error"}}}},
    "/api/v1/webhooks/paddle": {"post": {"tags": ["Internal webhook"], "operationId": "paddleWebhook", "parameters": [{"name": "Paddle-Signature", "in": "header", "required": true, "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object"}}}}, "responses": {"200": {"description": "Verified, persisted, and idempotently processed"}, "400": {"$ref": "#/components/responses/Error"}, "401": {"$ref": "#/components/responses/Error"}, "409": {"$ref": "#/components/responses/Error"}, "500": {"$ref": "#/components/responses/Error"}}}}
  },
  "components": {
    "securitySchemes": {
      "apiKey": {"type": "http", "scheme": "bearer", "bearerFormat": "ann_live_…"},
      "sessionCookie": {"type": "apiKey", "in": "cookie", "name": "__Host-annolux_session"},
      "csrf": {"type": "apiKey", "in": "header", "name": "X-CSRF-Token"}
    },
    "parameters": {
      "ReturnPath": {"name": "return", "in": "query", "required": false, "schema": {"type": "string", "pattern": "^/(?!/)"}, "description": "Safe relative path on https://annolux.com after OAuth."}
    },
    "responses": {
      "Error": {"description": "Stable error envelope", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}
    },
    "schemas": {
      "Health": {"type": "object", "required": ["status", "version"], "properties": {"status": {"type": "string", "enum": ["healthy", "ready"]}, "version": {"type": "string"}}},
      "SearchRequest": {
        "type": "object", "additionalProperties": false, "required": ["query"],
        "properties": {
          "query": {"type": "string", "minLength": 1, "maxLength": 500},
          "domains": {"type": "array", "maxItems": 20, "items": {"type": "string"}},
          "deduplicate": {"type": "boolean", "default": true},
          "limit": {"type": "integer", "minimum": 1, "maximum": 10, "default": 10},
          "timeout": {"type": "integer", "minimum": 1, "maximum": 30, "default": 30},
          "ranking": {"type": "string", "enum": ["default", "provider"], "default": "provider"}
        }
      },
      "SearchResult": {"type": "object", "additionalProperties": false, "required": ["rank", "title", "url", "fetched_at"], "properties": {"rank": {"type": "integer", "minimum": 1}, "score": {"type": "number"}, "title": {"type": "string"}, "url": {"type": "string", "format": "uri"}, "snippet": {"type": "string"}, "fetched_at": {"type": "string", "format": "date-time"}}},
      "SearchResponse": {"type": "object", "additionalProperties": false, "required": ["success", "query", "results", "deduplicated", "partial", "timing"], "properties": {"success": {"type": "boolean", "const": true}, "query": {"type": "string"}, "results": {"type": "array", "items": {"$ref": "#/components/schemas/SearchResult"}}, "deduplicated": {"type": "integer"}, "partial": {"type": "boolean"}, "timing": {"type": "object", "required": ["total_ms", "provider_ms", "enrichment_ms"], "properties": {"total_ms": {"type": "integer"}, "provider_ms": {"type": "integer"}, "enrichment_ms": {"type": "integer"}}}}},
      "ErrorDetail": {"type": "object", "required": ["code", "message"], "properties": {"code": {"type": "string"}, "message": {"type": "string"}}},
      "ErrorResponse": {"type": "object", "required": ["success", "error"], "properties": {"success": {"type": "boolean", "const": false}, "results": {"type": "array", "maxItems": 0}, "error": {"$ref": "#/components/schemas/ErrorDetail"}}},
      "Account": {"type": "object", "required": ["id", "status", "created_at", "terms_version", "refund_version", "csrf_token"], "properties": {"id": {"type": "string"}, "status": {"type": "string", "enum": ["active", "suspended", "closed"]}, "created_at": {"type": "string", "format": "date-time"}, "terms_version": {"type": "string"}, "refund_version": {"type": "string"}, "csrf_token": {"type": "string"}}},
      "APIKey": {"type": "object", "required": ["id", "prefix", "name", "created_at"], "properties": {"id": {"type": "string"}, "prefix": {"type": "string"}, "name": {"type": "string"}, "created_at": {"type": "string", "format": "date-time"}, "last_used_at": {"type": "string", "format": "date-time"}, "revoked_at": {"type": "string", "format": "date-time"}}},
      "CreditSummary": {"type": "object", "required": ["available", "granted", "used", "expired", "revoked", "reserved", "grants"], "properties": {"available": {"type": "integer", "minimum": 0}, "granted": {"type": "integer"}, "used": {"type": "integer"}, "expired": {"type": "integer"}, "revoked": {"type": "integer"}, "reserved": {"type": "integer"}, "grants": {"type": "array", "items": {"type": "object"}}}}
    }
  }
}
