{
  "openapi": "3.1.2",
  "info": {
    "title": "FetchMux Retrieval API",
    "version": "0.1.0",
    "summary": "Provider-neutral retrieval routing with auditable route receipts",
    "description": "This document defines the FetchMux gateway contract. It does not describe, replace, or imply\nendorsement by any upstream provider. Provider credentials remain operator-controlled in the\nfounding BYOK deployment.\n\n`/health` and `/ready` are public operational probes. Every `/v1/*` route requires a FetchMux\nbearer key unless the operator has deliberately set `FETCHMUX_AUTH_DISABLED=true` for a trusted\nlocal environment.\n"
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8787",
      "description": "Default local gateway"
    }
  ],
  "tags": [
    {
      "name": "Operations",
      "description": "Process health and provider readiness"
    },
    {
      "name": "Retrieval",
      "description": "Protected provider status, route preview, and retrieval execution"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Operations"
        ],
        "operationId": "getHealth",
        "summary": "Check whether the gateway process is serving requests",
        "security": [],
        "responses": {
          "200": {
            "description": "Gateway process is healthy",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/ready": {
      "get": {
        "tags": [
          "Operations"
        ],
        "operationId": "getReadiness",
        "summary": "Check whether at least one retrieval provider is configured",
        "security": [],
        "responses": {
          "200": {
            "description": "At least one provider is configured",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadinessEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "No retrieval provider is configured",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadinessEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/providers": {
      "get": {
        "tags": [
          "Retrieval"
        ],
        "operationId": "listProviders",
        "summary": "List safe provider availability and capability status",
        "description": "Returns configuration status and issues, never provider credentials.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Safe status for every supported provider",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProviderListEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/AuthNotConfigured"
          }
        }
      }
    },
    "/v1/route/preview": {
      "post": {
        "tags": [
          "Retrieval"
        ],
        "operationId": "previewRoute",
        "summary": "Preview eligible routes without calling an upstream provider",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchRequest"
              },
              "examples": {
                "freshFacts": {
                  "summary": "Quality-oriented fresh-facts route",
                  "value": {
                    "query": "latest Node.js stable release",
                    "task": "fresh_facts",
                    "priority": "quality",
                    "freshness": "30d",
                    "maxCostUsd": 0.02,
                    "maxLatencyMs": 8000,
                    "limit": 8
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ordered eligible candidates and inspectable score components",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutePreviewEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "503": {
            "$ref": "#/components/responses/AuthNotConfigured"
          }
        }
      }
    },
    "/v1/search": {
      "post": {
        "tags": [
          "Retrieval"
        ],
        "operationId": "search",
        "summary": "Execute one policy-routed retrieval request",
        "description": "Selects an eligible configured provider, executes inside the absolute deadline and budget,\nnormalizes evidence, and returns an auditable route receipt. Safe failover is limited to\nclassified retryable errors.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchRequest"
              },
              "examples": {
                "boundedSearch": {
                  "value": {
                    "query": "latest Node.js stable release",
                    "task": "fresh_facts",
                    "priority": "balanced",
                    "freshness": "30d",
                    "maxCostUsd": 0.02,
                    "maxLatencyMs": 8000,
                    "limit": 8
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Normalized evidence and route receipt",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "description": "A validated request could not satisfy a hard routing policy",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "Authentication, provider availability, or upstream completion is unavailable",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque FetchMux API key",
        "description": "A key configured in `FETCHMUX_API_KEY` or `FETCHMUX_API_KEYS`."
      }
    },
    "headers": {
      "RequestId": {
        "description": "Fastify request identifier for operational correlation",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid FetchMux bearer key",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "UNAUTHORIZED",
                "message": "A valid FetchMux API key is required"
              }
            }
          }
        }
      },
      "AuthNotConfigured": {
        "description": "Protected routes cannot start because FetchMux authentication has no key",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "NOT_READY",
                "message": "FetchMux authentication is not configured"
              }
            }
          }
        }
      },
      "InvalidRequest": {
        "description": "Request body failed FetchMux validation",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "VALIDATION_ERROR",
                "message": "Invalid retrieval request",
                "details": [
                  {
                    "field": "query",
                    "message": "Too small: expected string to have >=1 characters"
                  }
                ]
              }
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Request body exceeded 64 KiB",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "PAYLOAD_TOO_LARGE",
                "message": "The request body is too large"
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Safe generic failure with no upstream body or secret material",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "INTERNAL_ERROR",
                "message": "The request could not be completed"
              }
            }
          }
        }
      }
    },
    "schemas": {
      "HealthEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "version"
            ],
            "properties": {
              "status": {
                "const": "ok"
              },
              "version": {
                "type": "string"
              }
            }
          }
        }
      },
      "ReadinessEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "availableProviders"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "ready",
                  "not_ready"
                ]
              },
              "availableProviders": {
                "type": "array",
                "uniqueItems": true,
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ProviderListEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "providers"
            ],
            "properties": {
              "providers": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ProviderStatus"
                }
              }
            }
          }
        }
      },
      "ProviderStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "displayName",
          "available",
          "costConfigured",
          "supportedTasks",
          "issues"
        ],
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 64
          },
          "displayName": {
            "type": "string"
          },
          "available": {
            "type": "boolean",
            "description": "True when the provider has valid runtime configuration and can be selected."
          },
          "costConfigured": {
            "type": "boolean",
            "description": "True when the upstream API charge is configured or known to be zero."
          },
          "supportedTasks": {
            "type": "array",
            "uniqueItems": true,
            "items": {
              "$ref": "#/components/schemas/RetrievalTask"
            }
          },
          "issues": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SearchRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "query"
        ],
        "properties": {
          "query": {
            "type": "string",
            "minLength": 1,
            "maxLength": 400
          },
          "priority": {
            "$ref": "#/components/schemas/RetrievalPriority"
          },
          "task": {
            "$ref": "#/components/schemas/RetrievalTask"
          },
          "freshness": {
            "type": "string",
            "enum": [
              "24h",
              "7d",
              "30d",
              "1y"
            ]
          },
          "maxCostUsd": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 1000
          },
          "maxLatencyMs": {
            "type": "integer",
            "minimum": 250,
            "maximum": 120000,
            "default": 10000
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 8
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$"
          },
          "language": {
            "type": "string",
            "pattern": "^[a-z]{2,3}(?:-[a-z0-9]{2,8})?$"
          },
          "includeDomains": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Hostname"
            }
          },
          "excludeDomains": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Hostname"
            }
          },
          "providerAllowlist": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          "fetchContent": {
            "type": "boolean",
            "default": false
          }
        },
        "not": {
          "properties": {
            "includeDomains": {},
            "excludeDomains": {}
          },
          "required": [
            "includeDomains",
            "excludeDomains"
          ]
        }
      },
      "RetrievalPriority": {
        "type": "string",
        "enum": [
          "balanced",
          "quality",
          "cost",
          "latency"
        ],
        "default": "balanced"
      },
      "RetrievalTask": {
        "type": "string",
        "enum": [
          "balanced",
          "fresh_facts",
          "deep_research",
          "page_content",
          "scholarly"
        ],
        "default": "balanced"
      },
      "Hostname": {
        "type": "string",
        "pattern": "^(?:\\*\\.)?(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}$"
      },
      "RoutePreviewEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "candidates"
            ],
            "properties": {
              "candidates": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RouteCandidate"
                }
              }
            }
          }
        }
      },
      "RouteCandidate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "providerId",
          "estimatedCostUsd",
          "estimatedLatencyMs",
          "totalScore",
          "components",
          "reasonCodes"
        ],
        "properties": {
          "providerId": {
            "type": "string"
          },
          "estimatedCostUsd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "estimatedLatencyMs": {
            "type": "number",
            "minimum": 0
          },
          "totalScore": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "components": {
            "$ref": "#/components/schemas/RouteScoreComponents"
          },
          "reasonCodes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RouteReasonCode"
            }
          }
        }
      },
      "RouteScoreComponents": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "quality",
          "reliability",
          "cost",
          "latency"
        ],
        "properties": {
          "quality": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "reliability": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "cost": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "latency": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "SearchEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/SearchResponse"
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "results",
          "route"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NormalizedSearchResult"
            }
          },
          "route": {
            "$ref": "#/components/schemas/RouteReceipt"
          }
        }
      },
      "NormalizedSearchResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "url",
          "provider",
          "rank"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https?://"
          },
          "snippet": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20000
          },
          "publishedAt": {
            "type": "string",
            "format": "date-time"
          },
          "author": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "provider": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "rank": {
            "type": "integer",
            "minimum": 1
          },
          "providerScore": {
            "type": "number"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "RouteReceipt": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "selectedProvider",
          "attemptedProviders",
          "attempts",
          "reasonCodes",
          "estimatedCostUsd",
          "latencyMs",
          "fallbackUsed",
          "traceId"
        ],
        "properties": {
          "selectedProvider": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "attemptedProviders": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            }
          },
          "attempts": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/RouteAttempt"
            }
          },
          "reasonCodes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/RouteReasonCode"
            }
          },
          "estimatedCostUsd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "latencyMs": {
            "type": "number",
            "minimum": 0
          },
          "fallbackUsed": {
            "type": "boolean"
          },
          "traceId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        }
      },
      "RouteAttempt": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "provider",
          "startedAt",
          "endedAt",
          "latencyMs",
          "estimatedCostUsd",
          "outcome"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "endedAt": {
            "type": "string",
            "format": "date-time"
          },
          "latencyMs": {
            "type": "number",
            "minimum": 0
          },
          "estimatedCostUsd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "outcome": {
            "type": "string",
            "enum": [
              "success",
              "retryable_error",
              "terminal_error"
            ]
          },
          "errorCode": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          }
        }
      },
      "RouteReasonCode": {
        "type": "string",
        "enum": [
          "TASK_MATCH",
          "QUALITY_PRIORITY",
          "COST_PRIORITY",
          "LATENCY_PRIORITY",
          "BALANCED_PRIORITY",
          "RELIABILITY_WEIGHT",
          "WITHIN_BUDGET",
          "ONLY_ELIGIBLE_PROVIDER",
          "FALLBACK_AFTER_RETRYABLE_ERROR"
        ]
      },
      "ErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "traceId": {
                "type": "string"
              },
              "details": {
                "oneOf": [
                  {
                    "type": "object",
                    "additionalProperties": true
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ValidationIssue"
                    }
                  }
                ]
              }
            }
          }
        }
      },
      "ValidationIssue": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "field",
          "message"
        ],
        "properties": {
          "field": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  }
}
