{
  "openapi": "3.1.0",
  "info": {
    "title": "Palate Agent API",
    "version": "0.2.0",
    "description": "API-key-first access to public, unlisted, and authorized private Palates. The simplest path: create a Palate API key, send it in the palate-api-key header, and POST a question to /api/ask/query — the query spends the key owner's existing unlock or credit balance (no wallet, no quote step). Omit scope for global public search, use @handle mentions to narrow the search, or pass scope.kind=palate for one Palate. Choose mode=synthesized (default) for a grounded answer with citations, or mode=raw to get the ranked source chunks back and resynthesize them yourself. On-chain pay-per-query (x402 over Base, MPP over Tempo) is also supported for wallet-native agents without an account."
  },
  "servers": [
    {
      "url": "https://palate.inc",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Discovery"
    },
    {
      "name": "Quotes"
    },
    {
      "name": "Paid Queries"
    },
    {
      "name": "MCP"
    }
  ],
  "components": {
    "securitySchemes": {
      "PalateApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "palate-api-key",
        "description": "Palate API key created in account settings, sent as `palate-api-key: palate_...`. Paid queries spend the key owner's existing unlock or credit balance. This is the recommended path: no wallet and no quote step."
      },
      "PrivyBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Optional Privy bearer token. Required only when reading a private Palate as an authorized owner or team member."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "not_found",
              "missing_question",
              "rate_limited",
              "invalid_payment_proof"
            ]
          },
          "message": {
            "type": "string"
          },
          "resetAt": {
            "type": "string",
            "format": "date-time"
          },
          "supportedRails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SupportedRail"
            }
          }
        },
        "additionalProperties": true
      },
      "Channel": {
        "type": "object",
        "required": [
          "id",
          "kind",
          "label",
          "accessLevel",
          "ratePerMillionTokens",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "podcast",
              "rss",
              "notion"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "youtube",
              "podcast_rss",
              "rss",
              "notion",
              "subtext",
              "user_upload"
            ]
          },
          "label": {
            "type": "string",
            "examples": [
              "Invest Like the Best"
            ]
          },
          "accessLevel": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ]
          },
          "ratePerMillionTokens": {
            "type": "number",
            "description": "Dollars per one million Palate tokens."
          },
          "status": {
            "type": "string",
            "enum": [
              "live",
              "syncing",
              "paused"
            ]
          }
        },
        "additionalProperties": false
      },
      "EndpointManifest": {
        "type": "object",
        "required": [
          "channels",
          "quote",
          "query",
          "mcp"
        ],
        "properties": {
          "channels": {
            "type": "string"
          },
          "quote": {
            "type": "string"
          },
          "query": {
            "type": "string"
          },
          "mcp": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "DirectoryPalate": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "description",
          "channels",
          "endpoints"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Channel"
            }
          },
          "endpoints": {
            "$ref": "#/components/schemas/EndpointManifest"
          }
        },
        "additionalProperties": false
      },
      "PalateRef": {
        "type": "object",
        "required": [
          "slug",
          "name"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "QuoteRequest": {
        "type": "object",
        "description": "Static quote request body. Omit entirely when authenticating with a Palate API key. rail is required only when asking for on-chain price metadata.",
        "properties": {
          "rail": {
            "type": "string",
            "enum": [
              "tempo",
              "x402_base"
            ],
            "description": "On-chain payment rail. Required for on-chain price metadata; ignored when a Palate API key is present."
          }
        },
        "additionalProperties": false
      },
      "SupportedRail": {
        "type": "object",
        "required": [
          "rail",
          "currency",
          "feeSponsored"
        ],
        "properties": {
          "rail": {
            "type": "string",
            "enum": [
              "tempo",
              "x402_base"
            ]
          },
          "currency": {
            "type": "string",
            "examples": [
              "USDC"
            ]
          },
          "chain": {
            "type": "string",
            "examples": [
              "base"
            ]
          },
          "feeSponsored": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "QuoteResponse": {
        "description": "Static price metadata for on-chain rails, or account-balance cost for API-key calls. No nonce is minted.",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "rail",
              "amountUsdMillicents",
              "payTo",
              "currency",
              "chainId"
            ],
            "properties": {
              "rail": {
                "type": "string",
                "enum": [
                  "tempo",
                  "x402_base"
                ]
              },
              "amountUsdMillicents": {
                "type": "integer",
                "description": "Fixed answer price in USD millicents. Divide by 100000 for USD."
              },
              "payTo": {
                "type": "string",
                "description": "Rail-specific receiving address."
              },
              "currency": {
                "type": "string",
                "description": "Rail-specific USDC contract address."
              },
              "chainId": {
                "type": "integer",
                "description": "Tempo Moderato or Base chain id for this rail."
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "rail",
              "costUnlocks",
              "costCredits",
              "costUsd"
            ],
            "properties": {
              "rail": {
                "type": "string",
                "const": "api_key"
              },
              "costUnlocks": {
                "type": "integer"
              },
              "costCredits": {
                "type": "integer"
              },
              "costUsd": {
                "type": "number"
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "QueryScope": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "const": "global"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "kind",
              "slug"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "const": "palate"
              },
              "slug": {
                "type": "string",
                "minLength": 1
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "kind",
              "handles"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "const": "handles"
              },
              "handles": {
                "type": "array",
                "minItems": 1,
                "maxItems": 20,
                "items": {
                  "type": "string",
                  "minLength": 1
                }
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "QueryRequest": {
        "type": "object",
        "required": [
          "question"
        ],
        "properties": {
          "question": {
            "type": "string",
            "minLength": 1
          },
          "mode": {
            "type": "string",
            "enum": [
              "raw",
              "synthesized"
            ],
            "default": "synthesized",
            "description": "synthesized (default) returns a grounded answer with citations. raw returns a structured evidence set for agent-side synthesis."
          },
          "scope": {
            "$ref": "#/components/schemas/QueryScope",
            "description": "Only used on /api/ask/query. Omit for global search with @handle mention narrowing, or set global, palate, or handles explicitly."
          },
          "channelIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "paymentMode": {
            "type": "string",
            "enum": [
              "unlocks",
              "credits"
            ],
            "description": "Required when authenticating with a Palate API key. Chooses whether the query spends the key owner's unlocks or credits. Ignored for on-chain payment credential queries."
          },
          "rail": {
            "type": "string",
            "enum": [
              "tempo",
              "x402_base"
            ],
            "description": "Required for on-chain queries. The first unpaid call returns the rail-specific 402 challenge header: WWW-Authenticate for MPP/Tempo or PAYMENT-REQUIRED for x402."
          },
          "walletAddress": {
            "type": "string",
            "description": "Optional payer wallet hint. Required to consume a Tempo on-chain credit before issuing a new 402 challenge."
          },
          "_meta": {
            "type": "object",
            "description": "MCP metadata. Payment credentials may be supplied as _meta[\"org.paymentauth/credential\"].",
            "additionalProperties": true
          },
          "queryMode": {
            "type": "string",
            "enum": [
              "lookup",
              "summary",
              "trend_brief",
              "claim_extraction",
              "source_inventory",
              "comparison"
            ],
            "description": "Optional retrieval mode. Use summary, trend_brief, claim_extraction, source_inventory, or comparison for broader evidence tasks; omit for automatic lookup-oriented planning."
          }
        },
        "additionalProperties": false
      },
      "Citation": {
        "type": "object",
        "description": "A synthesized-answer citation as returned by the Worker.",
        "required": [
          "label",
          "text",
          "palateUrl",
          "itemId",
          "chunkId"
        ],
        "properties": {
          "label": {
            "type": "string",
            "description": "Human-facing source label, for example \"Source 1\"."
          },
          "text": {
            "type": "string",
            "description": "The cited chunk text."
          },
          "palateUrl": {
            "type": "string",
            "description": "Canonical Palate citation path, relative to https://palate.inc, of the form /<slug>/i/<itemId>?chunks=<chunkId>."
          },
          "itemId": {
            "type": "string"
          },
          "chunkId": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "RawChunk": {
        "type": "object",
        "description": "A ranked source chunk returned when mode=raw, for the agent to resynthesize.",
        "required": [
          "chunkId",
          "itemId",
          "channelId",
          "text",
          "score",
          "ordinal",
          "accessLevel",
          "palateUrl",
          "palateTokens"
        ],
        "properties": {
          "chunkId": {
            "type": "string"
          },
          "itemId": {
            "type": "string"
          },
          "channelId": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "score": {
            "type": "number",
            "description": "Rerank relevance score."
          },
          "ordinal": {
            "type": "integer"
          },
          "accessLevel": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ]
          },
          "palateUrl": {
            "type": "string",
            "description": "Canonical Palate citation path, relative to https://palate.inc, of the form /<slug>/i/<itemId>?chunks=<chunkId>."
          },
          "sourceUrl": {
            "type": "string",
            "format": "uri",
            "description": "Original source URL when the cited item is public. Omitted for private channels."
          },
          "publishedAt": {
            "type": "integer",
            "description": "Item publication time as a Unix epoch timestamp in milliseconds. Omitted when unknown."
          },
          "startSeconds": {
            "type": "number",
            "description": "Start offset in seconds for audio/video items."
          },
          "endSeconds": {
            "type": "number",
            "description": "End offset in seconds for audio/video items."
          },
          "palateTokens": {
            "type": "integer",
            "description": "Palate token count for this chunk (ceil(charCount / 4))."
          }
        },
        "additionalProperties": false
      },
      "QueryResponse": {
        "type": "object",
        "description": "Synthesized response (mode=synthesized, the default).",
        "required": [
          "answer",
          "citations",
          "queryId",
          "charge"
        ],
        "properties": {
          "answer": {
            "type": "string",
            "description": "Synthesized answer grounded in the returned citations."
          },
          "citations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Citation"
            }
          },
          "scope": {
            "$ref": "#/components/schemas/QueryScope"
          },
          "queryId": {
            "type": "string"
          },
          "charge": {
            "type": "string",
            "enum": [
              "onchain",
              "unlock",
              "credits"
            ]
          },
          "rail": {
            "type": "string",
            "enum": [
              "tempo",
              "x402_base"
            ],
            "description": "Present for on-chain payments."
          },
          "txHash": {
            "type": "string",
            "description": "Tempo tx hash or x402 facilitator receipt id for on-chain payments."
          },
          "receipt": {
            "description": "Payment receipt object/string returned for on-chain payments. Also sent in Payment-Receipt for MPP/Tempo or PAYMENT-RESPONSE for x402."
          }
        },
        "additionalProperties": false
      },
      "RawQueryResponse": {
        "type": "object",
        "description": "Raw response (mode=raw). Returns structured evidence for agent-side synthesis.",
        "required": [
          "mode",
          "chunks",
          "queryId",
          "charge"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "const": "raw"
          },
          "chunks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RawChunk"
            }
          },
          "scope": {
            "$ref": "#/components/schemas/QueryScope"
          },
          "queryId": {
            "type": "string"
          },
          "charge": {
            "type": "string",
            "enum": [
              "onchain",
              "unlock",
              "credits"
            ]
          },
          "rail": {
            "type": "string",
            "enum": [
              "tempo",
              "x402_base"
            ],
            "description": "Present for on-chain payments."
          },
          "txHash": {
            "type": "string",
            "description": "Tempo transaction hash, x402 settlement receipt id, or consumed Tempo credit id for on-chain payments."
          },
          "receipt": {
            "description": "Payment receipt object/string returned for on-chain payments. Also sent in Payment-Receipt for MPP/Tempo or PAYMENT-RESPONSE for x402."
          },
          "sourceGroups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceSourceGroup"
            }
          },
          "claims": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceClaim"
            }
          },
          "retrieval": {
            "type": "object",
            "properties": {
              "contextDepth": {
                "type": "string",
                "enum": [
                  "compact",
                  "expanded"
                ]
              },
              "queryMode": {
                "type": "string",
                "enum": [
                  "lookup",
                  "summary",
                  "trend_brief",
                  "claim_extraction",
                  "source_inventory",
                  "comparison"
                ]
              },
              "returnedChunkCount": {
                "type": "integer"
              },
              "noRelevantContent": {
                "type": "boolean"
              },
              "diagnostics": {
                "$ref": "#/components/schemas/RetrievalDiagnostics"
              },
              "sourceGroupCount": {
                "type": "integer"
              },
              "claimCount": {
                "type": "integer"
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "McpManifest": {
        "type": "object",
        "required": [
          "protocol",
          "palate",
          "tools",
          "skill"
        ],
        "properties": {
          "protocol": {
            "type": "string",
            "const": "mcp"
          },
          "palate": {
            "$ref": "#/components/schemas/PalateRef"
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "description"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "enum": [
                    "palate_list_channels",
                    "palate_quote",
                    "palate_query"
                  ]
                },
                "description": {
                  "type": "string"
                },
                "inputSchema": {
                  "type": "object",
                  "description": "JSON Schema for the MCP tool arguments.",
                  "additionalProperties": true
                }
              },
              "additionalProperties": false
            }
          },
          "skill": {
            "type": "string",
            "const": "/SKILL.md"
          }
        },
        "additionalProperties": false
      },
      "McpRequest": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "examples": [
              "2.0"
            ]
          },
          "id": {
            "description": "JSON-RPC request id.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "method": {
            "type": "string",
            "description": "Supports initialize, tools/list, tools/call, or direct tool names.",
            "enum": [
              "initialize",
              "notifications/initialized",
              "tools/list",
              "tools/call",
              "palate_list_channels",
              "palate_quote",
              "palate_query"
            ]
          },
          "params": {
            "type": "object",
            "description": "For tools/call, pass { name, arguments }. For direct tool methods, pass tool arguments.",
            "additionalProperties": true
          },
          "tool": {
            "type": "string",
            "description": "Compatibility alias for direct tool calls."
          },
          "arguments": {
            "type": "object",
            "description": "Compatibility field for direct tool arguments.",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "McpResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "description": "JSON-RPC response id.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "description": "MCP result. tools/call returns content plus structuredContent; direct tool methods return the tool payload.",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "data": {
                "additionalProperties": true
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "RetrievalDiagnostics": {
        "type": "object",
        "required": [
          "lowConfidence",
          "topItemShare",
          "topChannelShare",
          "candidateCount",
          "rerankCount",
          "uniqueSources",
          "missingAnchorGroups",
          "missingEntityGroups"
        ],
        "properties": {
          "lowConfidence": {
            "type": "boolean"
          },
          "topItemShare": {
            "type": "number"
          },
          "topChannelShare": {
            "type": "number"
          },
          "candidateCount": {
            "type": "integer"
          },
          "rerankCount": {
            "type": "integer"
          },
          "uniqueSources": {
            "type": "integer"
          },
          "missingAnchorGroups": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "missingEntityGroups": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        "additionalProperties": false
      },
      "EvidenceSourceGroup": {
        "type": "object",
        "required": [
          "sourceKey",
          "itemId",
          "channelId",
          "topScore",
          "chunkCount",
          "claimCount",
          "chunks"
        ],
        "properties": {
          "sourceKey": {
            "type": "string"
          },
          "itemId": {
            "type": "string"
          },
          "channelId": {
            "type": "string"
          },
          "sourceUrl": {
            "type": "string",
            "format": "uri"
          },
          "publishedAt": {
            "type": "integer"
          },
          "topScore": {
            "type": "number"
          },
          "chunkCount": {
            "type": "integer"
          },
          "claimCount": {
            "type": "integer"
          },
          "chunks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RawChunk"
            }
          }
        },
        "additionalProperties": false
      },
      "EvidenceClaim": {
        "type": "object",
        "required": [
          "claimId",
          "text",
          "chunkIds",
          "itemIds",
          "channelIds",
          "score",
          "reason"
        ],
        "properties": {
          "claimId": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "chunkIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "itemIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "channelIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "score": {
            "type": "number"
          },
          "reason": {
            "type": "string"
          }
        },
        "additionalProperties": false
      }
    }
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "summary": "Return Worker health",
        "responses": {
          "200": {
            "description": "Worker health response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "service",
                    "environment"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "service": {
                      "type": "string",
                      "const": "palate-edge"
                    },
                    "environment": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/api/palates": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "summary": "List public directory Palates",
        "description": "Returns a paginated list of public Palates that opted into the directory. Production returns an empty list instead of bundled demo rows if Convex is unavailable.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows to return. Defaults to 50 and caps at 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque pagination cursor returned as nextCursor. Current implementation uses a numeric offset string.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Directory rows with channels, rates, and endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DirectoryPalate"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Pass this as cursor to fetch the next page. Omitted on the last page."
                    }
                  },
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/api/palates/{slug}/channels": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "summary": "Return one Palate channel manifest",
        "description": "Public and unlisted Palates are readable by slug. Private Palates require an authorized Palate API key or Privy bearer token/cookie for the owner/team member.",
        "security": [
          {},
          {
            "PalateApiKey": []
          },
          {
            "PrivyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Channel manifest with dollars per one million Palate tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "slug",
                    "name",
                    "channels",
                    "pricingUnit",
                    "palateToken"
                  ],
                  "properties": {
                    "slug": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "channels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Channel"
                      }
                    },
                    "pricingUnit": {
                      "type": "string",
                      "const": "dollars_per_million_palate_tokens"
                    },
                    "palateToken": {
                      "type": "string",
                      "const": "ceil(charCount / 4)"
                    }
                  },
                  "additionalProperties": false
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate, private Palate without authorization, or production Convex miss.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/palates/{slug}/quote": {
      "post": {
        "tags": [
          "Quotes"
        ],
        "summary": "Return static answer price metadata",
        "description": "Quotes are free. With a valid Palate API key, send no body and the response returns the unlock/credit cost for the key owner's account. For on-chain rails, pass the requested rail and receive price, receiver address, currency, and chain id.",
        "security": [
          {},
          {
            "PalateApiKey": []
          },
          {
            "PrivyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "apiKey": {
                  "summary": "API key: omit the body (or send {}) to get the account-balance cost",
                  "value": {}
                },
                "onchain": {
                  "summary": "On-chain: request static rail metadata",
                  "value": {
                    "rail": "x402_base"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Static on-chain price metadata, or account-balance cost when using a Palate API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid rail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate or private Palate without authorization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quote rate limit exceeded. Retry-After is returned in seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Quote service unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/ask/quote": {
      "post": {
        "tags": [
          "Paid Queries"
        ],
        "summary": "Return static price metadata for a scoped agent query",
        "description": "Use this for static price metadata for global or multi-handle scoped queries paid by Tempo or x402 over Base. API-key callers can skip quote and POST directly to /api/ask/query with paymentMode. On-chain callers may also skip quote and use the 402 challenge flow directly.",
        "security": [
          {},
          {
            "PalateApiKey": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "x402_base": {
                  "value": {
                    "rail": "x402_base"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Static on-chain price metadata, or account-balance cost when using a Palate API key.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/QuoteResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyQuoteResponse"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/ask/query": {
      "post": {
        "tags": [
          "Paid Queries"
        ],
        "summary": "Run a scoped agent query globally, by Palate, or by handles",
        "description": "Same retrieval style as the homepage ask box. Omit scope for global public search with @handle and @creator/collection mention narrowing, set scope.kind=palate for one Palate, or scope.kind=handles for a few users. API-key queries spend account balance. On-chain Tempo/x402 queries use the standard 402 payment challenge flow: send rail, receive the rail challenge header, pay, then retry with the protocol credential in a payment header. Both API-key and on-chain queries support synthesized and raw modes.",
        "security": [
          {},
          {
            "PalateApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryRequest"
              },
              "examples": {
                "apiKeyGlobal": {
                  "summary": "Recommended: API key homepage-style query",
                  "value": {
                    "question": "What should I understand from @patrick this week?",
                    "paymentMode": "unlocks"
                  }
                },
                "apiKeyHandlesRaw": {
                  "summary": "API key raw chunks for selected handles",
                  "value": {
                    "question": "What do these experts say about pricing?",
                    "scope": {
                      "kind": "handles",
                      "handles": [
                        "patrick",
                        "april"
                      ]
                    },
                    "mode": "raw",
                    "paymentMode": "credits"
                  }
                },
                "onchainHandles": {
                  "summary": "On-chain scoped query over x402",
                  "value": {
                    "question": "What do @patrick and @april say about pricing?",
                    "scope": {
                      "kind": "handles",
                      "handles": [
                        "patrick",
                        "april"
                      ]
                    },
                    "rail": "x402_base",
                    "walletAddress": "0x..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scoped query response. mode=raw is available only with API-key authentication.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/QueryResponse"
                    },
                    {
                      "$ref": "#/components/schemas/RawQueryResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or invalid scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "On-chain payment required. Preflight has already run; answerable requests return the rail challenge header. Pay with the selected rail and retry with the protocol credential.",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "MPP/Tempo payment challenge."
              },
              "PAYMENT-REQUIRED": {
                "schema": {
                  "type": "string"
                },
                "description": "x402 v2 payment challenge."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate or handle scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No indexed public content was relevant enough to price this question.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/palates/{slug}/query": {
      "post": {
        "tags": [
          "Paid Queries"
        ],
        "summary": "Query a Palate (synthesized answer or raw chunks)",
        "description": "Optional one-Palate endpoint. For the default homepage-style global, @handle, and scoped search flow, use /api/ask/query. Send a Palate API key and paymentMode to spend the key owner's unlock/credit balance with no quote step. mode=synthesized (default) returns grounded prose with citations; mode=raw returns ranked source chunks. On-chain Tempo/x402 queries use the same 402 payment challenge flow as /api/ask/query: send rail, receive the rail challenge header, pay, then retry with the protocol credential in a payment header.",
        "security": [
          {},
          {
            "PalateApiKey": []
          },
          {
            "PrivyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryRequest"
              },
              "examples": {
                "apiKeySynthesized": {
                  "summary": "Recommended: API key, synthesized answer (paymentMode required)",
                  "value": {
                    "question": "What is Patrick's actual take on founder-led culture after a CEO transition?",
                    "paymentMode": "unlocks"
                  }
                },
                "apiKeyRaw": {
                  "summary": "API key, raw chunks to resynthesize (paymentMode required)",
                  "value": {
                    "question": "What is Patrick's actual take on founder-led culture after a CEO transition?",
                    "mode": "raw",
                    "paymentMode": "credits"
                  }
                },
                "tempo": {
                  "summary": "Advanced: on-chain payment over Tempo",
                  "value": {
                    "question": "What is Patrick's actual take on founder-led culture after a CEO transition?",
                    "rail": "tempo",
                    "walletAddress": "0x..."
                  }
                },
                "x402_base": {
                  "summary": "Advanced: on-chain payment over Base (x402)",
                  "value": {
                    "question": "What is Patrick's actual take on founder-led culture after a CEO transition?",
                    "rail": "x402_base",
                    "walletAddress": "0x..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "mode=synthesized returns a QueryResponse (answer + citations). mode=raw returns a RawQueryResponse (ranked source chunks).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/QueryResponse"
                    },
                    {
                      "$ref": "#/components/schemas/RawQueryResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing question.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "On-chain payment required. Preflight has already run; answerable requests return the rail challenge header. Pay with the selected rail and retry with the protocol credential.",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "MPP/Tempo payment challenge."
              },
              "PAYMENT-REQUIRED": {
                "schema": {
                  "type": "string"
                },
                "description": "x402 v2 payment challenge."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate or private Palate without authorization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No indexed Palate content was relevant enough to price this question.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Production retrieval unavailable, so the paid query cannot be priced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/{slug}/mcp": {
      "get": {
        "tags": [
          "MCP"
        ],
        "summary": "Return per-Palate MCP metadata",
        "description": "Returns the tool manifest for a single Palate. POST the same path with JSON-RPC MCP requests to list or call tools.",
        "security": [
          {},
          {
            "PalateApiKey": []
          },
          {
            "PrivyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "MCP tools for channel listing, quotes, and paid queries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpManifest"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate or private Palate without authorization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "MCP"
        ],
        "summary": "Call per-Palate MCP tools",
        "description": "Accepts MCP-style JSON-RPC requests. Use tools/list to inspect tools, tools/call with palate_list_channels, palate_quote, or palate_query, or call those tool names directly as method values. Send a Palate API key in the palate-api-key header to pay paid queries from the key owner's balance.",
        "security": [
          {},
          {
            "PalateApiKey": []
          },
          {
            "PrivyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/McpRequest"
              },
              "examples": {
                "toolsList": {
                  "summary": "List tools",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/list"
                  }
                },
                "quote": {
                  "summary": "Quote via tools/call",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "palate_quote",
                      "arguments": {
                        "rail": "x402_base"
                      }
                    }
                  }
                },
                "query": {
                  "summary": "Paid query via direct method",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "palate_query",
                    "params": {
                      "question": "What is the expert's view on founder-led culture?",
                      "rail": "x402_base",
                      "walletAddress": "0x...",
                      "_meta": {
                        "org.paymentauth/credential": "payment-credential-from-client"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MCP JSON-RPC response. Tool errors are returned in JSON-RPC error fields or as tools/call results with isError=true.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate or private Palate without authorization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported HTTP method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "get": {
        "tags": [
          "MCP"
        ],
        "summary": "Return the global Palate MCP manifest",
        "description": "Slug-less global MCP endpoint. palate_query supports scope.kind=global, palate, or handles.",
        "security": [
          {},
          {
            "PalateApiKey": []
          },
          {
            "PrivyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "MCP tools for channel listing, quotes, and paid queries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpManifest"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate or private Palate without authorization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "MCP"
        ],
        "summary": "Call global Palate MCP tools",
        "description": "Global MCP endpoint. palate_query accepts a scope argument (global, palate, or handles). On-chain payment uses JSON-RPC error -32042 with data.challenges and credential metadata at _meta[\"org.paymentauth/credential\"].",
        "security": [
          {},
          {
            "PalateApiKey": []
          },
          {
            "PrivyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/McpRequest"
              },
              "examples": {
                "toolsList": {
                  "summary": "List tools",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/list"
                  }
                },
                "quote": {
                  "summary": "Quote via tools/call",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "palate_quote",
                      "arguments": {
                        "rail": "x402_base"
                      }
                    }
                  }
                },
                "query": {
                  "summary": "Paid query via direct method",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "palate_query",
                    "params": {
                      "question": "What is the expert's view on founder-led culture?",
                      "rail": "x402_base",
                      "walletAddress": "0x...",
                      "_meta": {
                        "org.paymentauth/credential": "payment-credential-from-client"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MCP JSON-RPC response. Tool errors are returned in JSON-RPC error fields or as tools/call results with isError=true.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Palate or private Palate without authorization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported HTTP method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}
