{
  "openapi": "3.1.0",
  "info": {
    "title": "TaxWeave public website actions and documentation",
    "version": "1.1.0",
    "description": "Public documentation search and explicitly consented product updates signup. No product account or taxpayer data access. The /ask operation is read-only; /api/subscribe changes mailing-list membership and may trigger a welcome email. Read operation-specific consent, rate limit, and retry rules. Search API-Version defaults to 1; send API-Version: 1 to pin the contract. Breaking search changes will use a new major header value. Deprecation and Sunset response headers will announce withdrawal of a supported search major at least 90 days in advance. This policy applies to /ask; signup does not use this version header. The NLWeb response version is separate."
  },
  "servers": [
    {
      "url": "https://taxweave.ai"
    }
  ],
  "security": [],
  "paths": {
    "/ask": {
      "get": {
        "operationId": "searchPublicDocsGet",
        "summary": "Retrieve matching public documentation",
        "parameters": [
          {
            "in": "query",
            "name": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
            }
          },
          {
            "in": "query",
            "name": "streaming",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "in": "header",
            "name": "API-Version",
            "description": "Pin the public search API major. Omission defaults to 1; unsupported values return UNSUPPORTED_VERSION.",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching documents, or a NO_RESULTS failure. SSE uses start, result (index and item), and complete events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "_meta"
                  ],
                  "properties": {
                    "_meta": {
                      "type": "object",
                      "required": [
                        "response_type",
                        "version"
                      ],
                      "properties": {
                        "response_type": {
                          "enum": [
                            "answer",
                            "failure"
                          ]
                        },
                        "version": {
                          "type": "string",
                          "const": "0.55"
                        },
                        "response_format": {
                          "const": "conversational_search"
                        }
                      }
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "@type",
                          "name",
                          "url",
                          "description",
                          "grounding"
                        ],
                        "properties": {
                          "@type": {
                            "const": "WebPage"
                          },
                          "name": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "description": {
                            "type": "string"
                          },
                          "grounding": {
                            "type": "object",
                            "properties": {
                              "sources": {
                                "type": "array",
                                "items": {
                                  "type": "string",
                                  "format": "uri"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "API-Version": {
                "description": "Public search API contract major; currently 1.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "RateLimit-Limit": {
                "description": "Per-instance token bucket capacity.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              },
              "RateLimit-Remaining": {
                "description": "Whole tokens remaining after this request.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until this warm-instance token bucket is full if no further requests arrive.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                }
              }
            }
          },
          "400": {
            "description": "Malformed input or unsupported mode/format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "_meta": {
                    "response_type": "failure",
                    "version": "0.55"
                  },
                  "error": {
                    "code": "INVALID_QUERY",
                    "message": "query.text must contain 1 to 500 characters.",
                    "hint": "Send GET /ask?query=pricing or POST /ask with application/json and {\"query\":{\"text\":\"pricing\"}}."
                  }
                }
              }
            },
            "headers": {
              "API-Version": {
                "description": "Public search API contract major; currently 1.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "RateLimit-Limit": {
                "description": "Per-instance token bucket capacity.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              },
              "RateLimit-Remaining": {
                "description": "Whole tokens remaining after this request.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until this warm-instance token bucket is full if no further requests arrive.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "_meta": {
                    "response_type": "failure",
                    "version": "0.55"
                  },
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Please retry after a minute.",
                    "hint": "Wait for Retry-After before retrying."
                  }
                }
              }
            },
            "headers": {
              "API-Version": {
                "description": "Public search API contract major; currently 1.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "RateLimit-Limit": {
                "description": "Per-instance token bucket capacity.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              },
              "RateLimit-Remaining": {
                "description": "Whole tokens remaining after this request.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until this warm-instance token bucket is full if no further requests arrive.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "searchPublicDocs",
        "summary": "Retrieve source-grounded public documentation, optionally as SSE",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "object",
                    "required": [
                      "text"
                    ],
                    "properties": {
                      "text": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 500
                      }
                    }
                  },
                  "prefer": {
                    "type": "object",
                    "properties": {
                      "mode": {
                        "type": "string",
                        "enum": [
                          "list"
                        ]
                      },
                      "streaming": {
                        "type": "boolean",
                        "default": false
                      },
                      "response_format": {
                        "type": "string",
                        "enum": [
                          "conversational_search"
                        ]
                      }
                    }
                  }
                }
              },
              "example": {
                "query": {
                  "text": "What does TaxWeave cost?"
                },
                "prefer": {
                  "mode": "list",
                  "streaming": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching documents, or a NO_RESULTS failure. SSE uses start, result (index and item), and complete events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "_meta"
                  ],
                  "properties": {
                    "_meta": {
                      "type": "object",
                      "required": [
                        "response_type",
                        "version"
                      ],
                      "properties": {
                        "response_type": {
                          "enum": [
                            "answer",
                            "failure"
                          ]
                        },
                        "version": {
                          "type": "string",
                          "const": "0.55"
                        },
                        "response_format": {
                          "const": "conversational_search"
                        }
                      }
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "@type",
                          "name",
                          "url",
                          "description",
                          "grounding"
                        ],
                        "properties": {
                          "@type": {
                            "const": "WebPage"
                          },
                          "name": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "description": {
                            "type": "string"
                          },
                          "grounding": {
                            "type": "object",
                            "properties": {
                              "sources": {
                                "type": "array",
                                "items": {
                                  "type": "string",
                                  "format": "uri"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "API-Version": {
                "description": "Public search API contract major; currently 1.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "RateLimit-Limit": {
                "description": "Per-instance token bucket capacity.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              },
              "RateLimit-Remaining": {
                "description": "Whole tokens remaining after this request.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until this warm-instance token bucket is full if no further requests arrive.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                }
              }
            }
          },
          "400": {
            "description": "Malformed input or unsupported mode/format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "_meta": {
                    "response_type": "failure",
                    "version": "0.55"
                  },
                  "error": {
                    "code": "INVALID_QUERY",
                    "message": "query.text must contain 1 to 500 characters.",
                    "hint": "Send GET /ask?query=pricing or POST /ask with application/json and {\"query\":{\"text\":\"pricing\"}}."
                  }
                }
              }
            },
            "headers": {
              "API-Version": {
                "description": "Public search API contract major; currently 1.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "RateLimit-Limit": {
                "description": "Per-instance token bucket capacity.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              },
              "RateLimit-Remaining": {
                "description": "Whole tokens remaining after this request.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until this warm-instance token bucket is full if no further requests arrive.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "_meta": {
                    "response_type": "failure",
                    "version": "0.55"
                  },
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Please retry after a minute.",
                    "hint": "Wait for Retry-After before retrying."
                  }
                }
              }
            },
            "headers": {
              "API-Version": {
                "description": "Public search API contract major; currently 1.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "RateLimit-Limit": {
                "description": "Per-instance token bucket capacity.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              },
              "RateLimit-Remaining": {
                "description": "Whole tokens remaining after this request.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until this warm-instance token bucket is full if no further requests arrive.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "const": 60
                }
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "API-Version",
            "description": "Pin the public search API major. Omission defaults to 1; unsupported values return UNSUPPORTED_VERSION.",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          }
        ]
      }
    },
    "/api/subscribe": {
      "post": {
        "operationId": "subscribeToTaxweaveUpdates",
        "summary": "Subscribe an explicitly consenting person to product updates",
        "description": "Writes mailing-list membership and may send one welcome email. Only call after the person explicitly requests updates for the supplied email address. Research, pricing and booking are not consent. No credentials required. Does not reserve a Founding Firm spot or create a product account. Existing global/list opt-outs and pending/rejected preferences are preserved. Repeated requests reuse existing contacts, but there is no Idempotency-Key support or automatic replay guarantee. On network/502 errors completion may be uncertain; do not retry automatically. Rate limiting is per warm instance: 10 requests per 10 minutes per IP, and 3 per 10 minutes per normalized email. Distributed enforcement is not guaranteed. API-Version applies only to /ask.",
        "security": [],
        "x-webmcp-tool": "subscribe_to_taxweave_updates",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "updatesConsent",
                  "form",
                  "page"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "minLength": 6,
                    "maxLength": 254,
                    "description": "Authorized email address. Trimmed and lowercased by the server; quotes and backslashes are rejected."
                  },
                  "updatesConsent": {
                    "type": "boolean",
                    "const": true,
                    "description": "Set true only after explicit permission for this address to receive TaxWeave product updates."
                  },
                  "form": {
                    "type": "string",
                    "const": "updates"
                  },
                  "page": {
                    "type": "string",
                    "maxLength": 200,
                    "pattern": "^/(?!/)[A-Za-z0-9/_-]*(?:\\.html)?$",
                    "description": "Path only, without query or fragment. Use /agents for direct agent requests.",
                    "examples": [
                      "/agents"
                    ]
                  },
                  "website": {
                    "type": "string",
                    "const": "",
                    "description": "Honeypot. Omit or leave empty; nonempty submissions are discarded without subscription."
                  }
                },
                "example": {
                  "email": "person@example.com",
                  "updatesConsent": true,
                  "form": "updates",
                  "page": "/agents",
                  "website": ""
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Inspect updatesStatus. subscribed means the contact is on the list; preferences_preserved means existing opt-outs were respected, not a new subscription. A nonempty honeypot yields a decoy response without writing; callers must omit it or send empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "updatesStatus"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "updatesStatus": {
                      "enum": [
                        "subscribed",
                        "preferences_preserved"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON/content type, body over the 8,192-character limit, invalid email, missing explicit consent, or invalid form/path.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "bad_content_type",
                        "body_too_large",
                        "bad_json",
                        "bad_request",
                        "invalid_email",
                        "consent_required",
                        "invalid_source",
                        "rate_limited",
                        "subscribe_failed"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; respect Retry-After. Do not retry in a loop.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "const": 60
                },
                "description": "Minimum seconds to wait; further rate limits are possible."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "bad_content_type",
                        "body_too_large",
                        "bad_json",
                        "bad_request",
                        "invalid_email",
                        "consent_required",
                        "invalid_source",
                        "rate_limited",
                        "subscribe_failed"
                      ]
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Provider/configuration failure; signup may have partially completed. Do not claim success or retry automatically.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "bad_content_type",
                        "body_too_large",
                        "bad_json",
                        "bad_request",
                        "invalid_email",
                        "consent_required",
                        "invalid_source",
                        "rate_limited",
                        "subscribe_failed"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Meta": {
        "type": "object",
        "required": [
          "response_type",
          "version"
        ],
        "properties": {
          "response_type": {
            "enum": [
              "answer",
              "failure"
            ]
          },
          "version": {
            "type": "string",
            "const": "0.55"
          },
          "response_format": {
            "const": "conversational_search"
          }
        }
      },
      "Document": {
        "type": "object",
        "required": [
          "@type",
          "name",
          "url",
          "description",
          "grounding"
        ],
        "properties": {
          "@type": {
            "const": "WebPage"
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string"
          },
          "grounding": {
            "type": "object",
            "properties": {
              "sources": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      },
      "Response": {
        "type": "object",
        "required": [
          "_meta"
        ],
        "properties": {
          "_meta": {
            "$ref": "#/components/schemas/Meta"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Document"
            }
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "INVALID_QUERY",
              "BAD_JSON",
              "UNSUPPORTED_MEDIA_TYPE",
              "REQUEST_TOO_LARGE",
              "UNSUPPORTED_VERSION",
              "RATE_LIMITED",
              "NO_RESULTS",
              "NOT_FOUND"
            ]
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string",
            "description": "Actionable recovery guidance when available."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "_meta": {
            "type": "object",
            "required": [
              "response_type",
              "version"
            ],
            "properties": {
              "response_type": {
                "type": "string",
                "const": "failure"
              },
              "version": {
                "type": "string",
                "const": "0.55"
              }
            }
          },
          "error": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "responses": {
      "SearchResponse": {
        "description": "Matching documents, or a NO_RESULTS failure. SSE uses start, result (index and item), and complete events.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Response"
            }
          },
          "text/event-stream": {
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "InvalidRequest": {
        "description": "Malformed input or unsupported mode/format.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Response"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Response"
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Public documentation and API quickstart",
    "url": "https://taxweave.ai/docs"
  }
}
