{
  "info": {
    "name": "Kasva",
    "description": "Generated from docs/app/openapi.yaml (3.1.0). Re-run npm run docs:postman after OpenAPI changes.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "item": [
    {
      "name": "Meta",
      "item": [
        {
          "name": "GET /health",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/health",
            "description": "Liveness"
          }
        },
        {
          "name": "GET /v1",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1",
            "description": "Gateway index"
          }
        }
      ]
    },
    {
      "name": "Keys",
      "item": [
        {
          "name": "POST /v1/keys",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/keys",
            "description": "One-shot email key without a password account. Prefer\n`POST /v1/auth/register`. IP limit 5/hour. Second issue for the same\nemail is 409. Rotate with `POST /v1/auth/rotate-key`.\n",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\"\n}"
            }
          }
        },
        {
          "name": "GET /v1/auth/keys",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/keys",
            "description": "Mask only. Never returns the full `kv_…` secret.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "POST /v1/auth/keys",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/auth/keys",
            "description": "`name` required, 1–64 characters. Full `api_key` once. Duplicate\nactive name → 409. Max 10 active keys → 400.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"cursor\"\n}"
            }
          }
        },
        {
          "name": "GET /v1/auth/keys/{id}",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/keys/:id",
            "description": "One named key (mask only)",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "DELETE /v1/auth/keys/{id}",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/keys/:id",
            "description": "Last remaining key → 400. Canonical rotate remains `POST /v1/auth/rotate-key`.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Auth",
      "item": [
        {
          "name": "POST /v1/auth/register",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/auth/register",
            "description": "Create account and gateway key",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\",\n  \"password\": \"at-least-8-chars\",\n  \"name\": \"Optional\",\n  \"accepted_terms\": true\n}"
            }
          }
        },
        {
          "name": "POST /v1/auth/login",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/auth/login",
            "description": "Sign in",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\",\n  \"password\": \"at-least-8-chars\"\n}"
            }
          }
        },
        {
          "name": "POST /v1/auth/logout",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/logout",
            "description": "Sign out",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "POST /v1/auth/logout-all",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/logout-all",
            "description": "Keeps the current cookie.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "GET /v1/auth/sessions",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/sessions",
            "description": "List active sessions",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "GET /v1/auth/me",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/me",
            "description": "Current user",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "POST /v1/auth/forgot-password",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/auth/forgot-password",
            "description": "Valid-shaped emails always get the same generic 200.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\"\n}"
            }
          }
        },
        {
          "name": "POST /v1/auth/reset-password",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/auth/reset-password",
            "description": "Complete password reset",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"…\",\n  \"password\": \"new-password-8+\"\n}"
            }
          }
        },
        {
          "name": "POST /v1/auth/verify-email/request",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/verify-email/request",
            "description": "API stays usable unverified. No-op when mail is unset."
          }
        },
        {
          "name": "POST /v1/auth/verify-email",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/auth/verify-email",
            "description": "Confirm email",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"…\"\n}"
            }
          }
        },
        {
          "name": "POST /v1/auth/rotate-key",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/rotate-key",
            "description": "Session cookie or Bearer. Returns a new `api_key` once; the previous\nkey stops immediately. Stored BYOK moves to the new key. Limit 5/hour.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "POST /v1/auth/claim-account",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/auth/claim-account",
            "description": "Same `kv_…` key is kept. `accepted_terms` required.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\",\n  \"api_key\": \"kv_…\",\n  \"password\": \"at-least-8-chars\",\n  \"accepted_terms\": true\n}"
            }
          }
        },
        {
          "name": "GET /v1/auth/oauth/github",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/oauth/github",
            "description": "Start GitHub OAuth"
          }
        },
        {
          "name": "POST /v1/auth/oauth/github",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/oauth/github",
            "description": "Start GitHub OAuth"
          }
        },
        {
          "name": "GET /v1/auth/oauth/google",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/oauth/google",
            "description": "Start Google OAuth"
          }
        },
        {
          "name": "POST /v1/auth/oauth/google",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/oauth/google",
            "description": "Start Google OAuth"
          }
        },
        {
          "name": "GET /v1/auth/oauth/{provider}/callback",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/auth/oauth/:provider/callback",
            "description": "OAuth return"
          }
        }
      ]
    },
    {
      "name": "BYOK",
      "item": [
        {
          "name": "GET /v1/byok/keys",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/byok/keys",
            "description": "Never returns secrets or last-4.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "POST /v1/byok/keys",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/byok/keys",
            "description": "Omit a field to leave it unchanged. Send `\"\"` to delete that provider.\n`{ \"enabled\": { \"openai\": false } }` disables a stored key without\nresending it. Per-request provider headers are not accepted.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"groq\": \"gsk_...\",\n  \"openai\": \"sk-...\"\n}"
            }
          }
        }
      ]
    },
    {
      "name": "Models",
      "item": [
        {
          "name": "GET /v1/models",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/models",
            "description": "OpenAI-compatible list. BYOK ids appear only for providers you have\nregistered and not disabled. Hosted ids are `kasva/<catalog-id>`.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Chat",
      "item": [
        {
          "name": "POST /v1/chat/completions",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/chat/completions",
            "description": "`stream: true` pipes OpenAI-compatible SSE. Response includes a\n`kasva` routing object. Image and video model ids are rejected\nhere — use `POST /v1/images` or `POST /v1/videos`.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"model\": \"kasva/auto\",\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"Hello\"\n    }\n  ],\n  \"temperature\": 0.7,\n  \"max_tokens\": 1024,\n  \"stream\": false\n}"
            }
          }
        },
        {
          "name": "POST /v1/messages",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/messages",
            "description": "Anthropic-shaped request and response. Anthropic SDKs use base URL\n`https://kasva.dev`. The path is `/v1/messages`. Auth is Bearer\nand/or `x-api-key` with the Kasva `kv_…` key (not a provider `sk-ant-…`\nkey). Bearer wins if both are sent. `anthropic-version` is optional.\nRequired: `model`, `messages`, `max_tokens`.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"model\": \"kasva/auto\",\n  \"max_tokens\": 1024,\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"Hello\"\n    }\n  ]\n}"
            }
          }
        }
      ]
    },
    {
      "name": "Images",
      "item": [
        {
          "name": "POST /v1/images",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/images",
            "description": "Hosted `kasva/image` and `kasva/image-pro` (operator, credits debit after\nsuccess) plus BYOK generate. Prompt-only hosted generate (`n=1`). BYOK\nmay add `input_references` to edit. Image models are not accepted on chat.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"model\": \"kasva/image\",\n  \"prompt\": \"A product photo of a ceramic mug\"\n}"
            }
          }
        },
        {
          "name": "POST /v1/images/generations",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/images/generations",
            "description": "Alias of POST /v1/images",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"model\": \"byok/fal/flux-schnell\",\n  \"prompt\": \"A ceramic mug\"\n}"
            }
          }
        }
      ]
    },
    {
      "name": "Videos",
      "item": [
        {
          "name": "POST /v1/videos",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{baseUrl}}/v1/videos",
            "description": "Hosted `kasva/video`. Returns `queued` with id prefix `vid_`. Poll\n`GET /v1/videos/{id}` then fetch bytes from\n`GET /v1/videos/{id}/content` when `completed`. Rate-limit applies to\ncreate only. Optional `seconds` is an integer 4–15 (default 5).\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"model\": \"kasva/video\",\n  \"prompt\": \"A ceramic mug spinning on a table\",\n  \"seconds\": 5\n}"
            }
          }
        },
        {
          "name": "GET /v1/videos/{id}",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/videos/:id",
            "description": "Owner-only. Unknown or other-user ids are 404. Terminal jobs return\nstored status with no upstream poll. Queued and in-progress jobs poll\nonce per GET.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "GET /v1/videos/{id}/content",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/videos/:id/content",
            "description": "MP4 bytes for the owner when status is `completed`. Otherwise 404.\nNo JSON body.\n",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Usage",
      "item": [
        {
          "name": "GET /v1/usage",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/usage",
            "description": "Per-key usage stats",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Billing",
      "item": [
        {
          "name": "GET /v1/credits",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/credits",
            "description": "Credit balance",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "GET /v1/credits/history",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/credits/history",
            "description": "Credit pack grant history",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "POST /v1/credits/checkout",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/credits/checkout",
            "description": "Credits pack checkout URL",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        },
        {
          "name": "POST /v1/billing/pro",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/v1/billing/pro",
            "description": "Pro $9/month checkout URL",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "MCP",
      "item": [
        {
          "name": "GET /v1/mcp/tools",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/v1/mcp/tools",
            "description": "Catalog only; no remote execution.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{apiKey}}",
                  "type": "string"
                }
              ]
            }
          }
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://kasva.dev"
    },
    {
      "key": "apiKey",
      "value": "YOUR_API_KEY"
    },
    {
      "key": "baseUrl_1",
      "value": "http://127.0.0.1:8787"
    }
  ]
}
