{
  "openapi": "3.1.0",
  "info": {
    "title": "Youmeverse API",
    "version": "1.0.0",
    "description": "Youmeverse's Supabase Edge Function endpoints. Every endpoint below requires a Supabase user session (bearer access token) — there is no anonymous, API-key, or service-account access for third-party callers. All are server-authoritative: token-wallet debits happen before the upstream call and are refunded automatically if it fails, so a caller can never be charged for a failed request."
  },
  "servers": [
    { "url": "https://gdpoqavqcnvkmptyxtbv.supabase.co/functions/v1", "description": "Production" }
  ],
  "security": [{ "supabaseAuth": [] }],
  "paths": {
    "/chat": {
      "post": {
        "summary": "Send a message to the active assistant persona",
        "description": "A thin, streaming passthrough to an OpenRouter chat-completions request. The request body must already be a valid OpenRouter/OpenAI-compatible chat-completions payload (model + messages); the server injects a web-search plugin and clamps max_tokens server-side, then streams the completion back unchanged. Debits one chatMessage token before calling upstream; refunds automatically on any upstream failure.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["model", "messages"],
                "properties": {
                  "model": { "type": "string" },
                  "messages": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "role": { "type": "string", "enum": ["system", "user", "assistant"] },
                        "content": { "type": "string" }
                      }
                    }
                  },
                  "stream": { "type": "boolean", "default": true }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Streamed OpenRouter-compatible chat completion" },
          "402": { "description": "Insufficient token wallet balance" }
        }
      }
    },
    "/tts": {
      "post": {
        "summary": "Synthesize speech in a persona's configured voice",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["text", "personaId"],
                "properties": {
                  "text": { "type": "string" },
                  "personaId": { "type": "string", "description": "e.g. amina, eric, mireille, marcus_aurelius" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "audio/mpeg stream", "content": { "audio/mpeg": {} } },
          "402": { "description": "Insufficient token wallet balance" }
        }
      }
    },
    "/stt": {
      "post": {
        "summary": "Transcribe recorded speech to text",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["audio", "format"],
                "properties": {
                  "audio": { "type": "string", "description": "base64-encoded audio" },
                  "format": { "type": "string", "description": "e.g. m4a, webm, mp3" },
                  "language": { "type": "string", "description": "optional ISO language hint, e.g. en or fr" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transcription result",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "text": { "type": "string" } } } } }
          }
        }
      }
    },
    "/books-ask": {
      "post": {
        "summary": "Ask a question about a purchased narrated book",
        "description": "Retrieval-only endpoint: embeds the question and returns the most relevant, spoiler-safe book chunks (gated by the reader's own playback cursor and purchase ownership). The caller is expected to build the actual answer prompt from the returned chunks, matching this project's pattern of never assembling prompts server-side.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["bookId", "question"],
                "properties": {
                  "bookId": { "type": "string", "format": "uuid" },
                  "question": { "type": "string", "maxLength": 2000 },
                  "maxChunkIdx": { "type": "integer", "description": "Spoiler guard — caller's current reading cursor" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Matched book chunks + metadata" },
          "402": { "description": "Insufficient token wallet balance" },
          "403": { "description": "Book not owned by the caller" }
        }
      }
    },
    "/daily-gift": {
      "post": {
        "summary": "Claim the daily random token gift",
        "description": "Rate-limited to one claim per cooldown window (atomic server-side check — cannot be raced or replayed).",
        "responses": {
          "200": {
            "description": "Gift result",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "granted": { "type": "integer" } } } } }
          },
          "429": { "description": "Already claimed within the cooldown window" }
        }
      }
    },
    "/web-push-send": {
      "post": {
        "summary": "Send a test web-push notification to the caller's own installed PWA(s)",
        "description": "Sends only to the authenticated caller's own push subscriptions — there is no cross-user broadcast in this endpoint.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["title"],
                "properties": {
                  "title": { "type": "string" },
                  "body": { "type": "string" },
                  "url": { "type": "string", "description": "deep link to focus/open on click" },
                  "tag": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Delivery result", "content": { "application/json": { "schema": { "type": "object", "properties": { "sent": { "type": "integer" }, "pruned": { "type": "integer" } } } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "supabaseAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A Supabase Auth access token for the signed-in user (phone OTP / email / anonymous). Obtained by the user's own client, not issuable to a third party by Youmeverse."
      }
    }
  }
}
