{
  "openapi": "3.1.0",
  "info": {
    "title": "ComfyAuth API",
    "version": "0.3.0",
    "description": "Headless email/password authentication. Custom API, not OAuth/OIDC. All responses use Cache-Control: no-store. Maximum request body: 16 KiB. Bearer credentials only; the API does not set or accept auth cookies. API keys authorize administration, never a user login."
  },
  "servers": [
    {
      "url": "/",
      "description": "Current HTTPS deployment; use http://127.0.0.1:8080 for local development"
    }
  ],
  "paths": {
    "/v1/auth/signup": {
      "post": {
        "operationId": "signup",
        "summary": "Create an unverified account",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "202": {
            "description": "Generic acceptance, independent of account existence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "ASCII address, trimmed and lowercased. Plus suffixes and dots are preserved."
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 15,
                    "maxLength": 128,
                    "description": "Provisional signup password; at most 512 UTF-8 bytes. The email owner sets the final password when verifying."
                  },
                  "username": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "description": "Creates an unverified account with a provisional password. The email owner must choose the final password on the verification page before login is possible. Existing accounts receive the same generic response."
      }
    },
    "/v1/auth/login": {
      "post": {
        "operationId": "login",
        "summary": "Log in with a fixed session or opt-in automatic renewal",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Login"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "ASCII address, trimmed and lowercased. Plus suffixes and dots are preserved."
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "maxLength": 512
                  },
                  "renewable": {
                    "type": "boolean",
                    "default": false,
                    "description": "Opt in to a short-lived access token and single-use rotating refresh token."
                  }
                }
              }
            }
          }
        },
        "description": "Omit renewable or set false for the existing fixed 24-hour session. Set renewable:true for rotating refresh credentials. Project defaults are 15-minute access, seven-day refresh inactivity and 30-day absolute lifetime. Project Settings permit access 5\u201360 minutes, inactivity 1\u201390 days and absolute lifetime 1\u2013365 days, with inactivity no longer than absolute lifetime. These are custom ComfyAuth credentials, not OAuth/OIDC tokens."
      }
    },
    "/v1/auth/refresh": {
      "post": {
        "operationId": "refreshSession",
        "summary": "Rotate a refresh token and issue a new access token",
        "description": "Send the current refresh token once. An access token or server API key is not needed. A successful rotation invalidates the old access token and consumes the refresh token, returning new tokens for the same session ID. Replaying a consumed refresh token revokes that session family, including its current access/refresh tokens. Unknown, expired, revoked, wrong-project and disabled-account refresh credentials are rejected. Serialize refresh calls per session across workers/tabs. A lost response requires login again; blindly retrying the old token triggers replay revocation. Project policy applies at refresh: use the tighter current/stored idle and absolute limits; the original absolute cap is never extended. Previously issued access tokens retain their expiry until rotated or revoked.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "refresh_token"
                ],
                "properties": {
                  "refresh_token": {
                    "type": "string",
                    "pattern": "^ca_r_[A-Za-z0-9_-]{43}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New access and refresh credentials for the same session family",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RenewedSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/v1/auth/email/verification-request": {
      "post": {
        "operationId": "requestVerification",
        "summary": "Request a new verification link",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "202": {
            "description": "Generic acceptance, independent of account existence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "ASCII address, trimmed and lowercased. Plus suffixes and dots are preserved."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/email/verify": {
      "post": {
        "operationId": "verifyEmail",
        "summary": "Verify email and set the final password chosen by its owner",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Verified"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "token",
                  "password"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "pattern": "^ca_v_[A-Za-z0-9_-]{43}$"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 15,
                    "maxLength": 128,
                    "description": "Final password chosen by the email owner; at most 512 UTF-8 bytes. Replaces the provisional signup credential."
                  }
                }
              }
            }
          }
        },
        "description": "Submit the single-use emailed token with the final password chosen on the verification page. Atomically replaces the provisional signup password, verifies email, and revokes sessions and outstanding action tokens. Token-only requests are rejected."
      }
    },
    "/v1/auth/password/reset-request": {
      "post": {
        "operationId": "requestPasswordReset",
        "summary": "Request a reset for a verified active account",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "202": {
            "description": "Generic acceptance, independent of account existence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "ASCII address, trimmed and lowercased. Plus suffixes and dots are preserved."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/password/reset": {
      "post": {
        "operationId": "resetPassword",
        "summary": "Consume a reset token and revoke all sessions",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "204": {
            "description": "Success, no response body"
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "token",
                  "password"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "pattern": "^ca_v_[A-Za-z0-9_-]{43}$"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 15,
                    "maxLength": 128,
                    "description": "Maximum 512 UTF-8 bytes. Exact input is hashed; no normalization or trimming."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/password/change": {
      "post": {
        "operationId": "changePassword",
        "summary": "Change password and revoke all sessions including this session",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "204": {
            "description": "Success, no response body"
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "current_password",
                  "new_password"
                ],
                "properties": {
                  "current_password": {
                    "type": "string",
                    "format": "password",
                    "maxLength": 512
                  },
                  "new_password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 15,
                    "maxLength": 128,
                    "description": "Maximum 512 UTF-8 bytes. Exact input is hashed; no normalization or trimming."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/logout": {
      "post": {
        "operationId": "logout",
        "summary": "Revoke the current session",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "204": {
            "description": "Success, no response body"
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/v1/auth/logout-all": {
      "post": {
        "operationId": "logoutAll",
        "summary": "Revoke all sessions for this user",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "204": {
            "description": "Success, no response body"
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "currentUser",
        "summary": "Validate a session and return its user",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/v1/sessions": {
      "get": {
        "operationId": "listSessions",
        "summary": "List at most 100 active sessions, newest first",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/v1/sessions/{id}": {
      "delete": {
        "operationId": "revokeSession",
        "summary": "Revoke an owned session; unknown IDs are a no-op",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Success, no response body"
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/v1/admin/users/{id}/disable": {
      "post": {
        "operationId": "disableUser",
        "summary": "Disable a user and revoke their sessions and action tokens",
        "security": [
          {
            "serverKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectID"
          },
          {
            "$ref": "#/components/parameters/BrowserProjectID"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Success, no response body"
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "operationId": "health",
        "summary": "Liveness",
        "security": [],
        "responses": {
          "200": {
            "description": "Healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "const": "ok"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/readyz": {
      "get": {
        "operationId": "readiness",
        "summary": "Database and deployment configuration readiness",
        "security": [],
        "responses": {
          "200": {
            "description": "Healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "const": "ok"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Database unavailable, or email is not configured in the Vercel entrypoint"
          }
        }
      }
    },
    "/v1/{rest}": {
      "options": {
        "operationId": "corsPreflight",
        "summary": "CORS preflight for any v1 API path (rest may include slashes)",
        "security": [],
        "parameters": [
          {
            "name": "rest",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "project_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Allowed origin; returns CORS headers"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "User": {
        "type": "object",
        "required": [
          "id",
          "email",
          "username",
          "email_verified",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          },
          "email_verified": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Session": {
        "type": "object",
        "required": [
          "id",
          "created_at",
          "expires_at",
          "access_expires_at",
          "current"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Stored latest usable deadline: the later of current access expiry and the earlier stored refresh-inactivity/absolute deadlines; for fixed sessions, the access expiry. Policy changes are enforced at refresh. A refreshable family remains listed after its current access token expires."
          },
          "current": {
            "type": "boolean"
          },
          "access_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Expiry of the current access token. This can precede a renewable session family expiry."
          }
        }
      },
      "Login": {
        "type": "object",
        "required": [
          "session_token",
          "token_type",
          "expires_at",
          "user"
        ],
        "properties": {
          "session_token": {
            "type": "string",
            "pattern": "^ca_s_[A-Za-z0-9_-]{43}$"
          },
          "token_type": {
            "const": "Bearer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Expiry of this access/session token. Renewable access follows project policy (15 minutes by default, 5\u201360 minutes allowed) and is capped by the family deadline; fixed sessions last 24 hours."
          },
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "refresh_token": {
            "type": "string",
            "pattern": "^ca_r_[A-Za-z0-9_-]{43}$",
            "description": "Present only for renewable login/refresh. Secret, single-use, project-bound token. Replace both tokens atomically after refresh; never retry an uncertain refresh with the previous token."
          },
          "refresh_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Present for renewable sessions. Current refresh inactivity deadline under project policy (seven days by default), capped by session_expires_at."
          },
          "session_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Present for renewable sessions. Absolute session-family expiry set under project policy at login (30 days by default); current shorter policy may reduce it, but rotation never extends the original cap."
          }
        }
      },
      "Accepted": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "SessionList": {
        "type": "object",
        "required": [
          "sessions"
        ],
        "properties": {
          "sessions": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/Session"
            }
          }
        }
      },
      "Verified": {
        "type": "object",
        "required": [
          "email_verified"
        ],
        "properties": {
          "email_verified": {
            "const": true
          }
        }
      },
      "RenewedSession": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Login"
          },
          {
            "type": "object",
            "required": [
              "refresh_token",
              "refresh_expires_at",
              "session_expires_at"
            ]
          }
        ]
      },
      "ProjectSessionPolicy": {
        "type": "object",
        "description": "Renewal settings used by the developer portal project form. Project management is not a public /v1 CRUD API. Inactivity must not exceed absolute lifetime. Security rotation/revocation cannot be disabled.",
        "properties": {
          "access_token_minutes": {
            "type": "integer",
            "minimum": 5,
            "maximum": 60,
            "default": 15
          },
          "refresh_idle_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 90,
            "default": 7
          },
          "session_max_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "default": 30
          }
        }
      }
    },
    "parameters": {
      "ProjectID": {
        "name": "X-Project-ID",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Public project identifier, not a credential."
      },
      "BrowserProjectID": {
        "name": "project_id",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "For direct browser CORS requests, include this ID for preflight routing. It must equal X-Project-ID on the actual request."
      }
    },
    "securitySchemes": {
      "session": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ca_s_ opaque session"
      },
      "serverKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ca_k_ opaque server-only API key"
      }
    },
    "responses": {
      "Error400": {
        "description": "Invalid input, password policy, or action token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error401": {
        "description": "Invalid credentials, session, key, or origin",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error404": {
        "description": "Resource or project not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error429": {
        "description": "Rate limit exceeded",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Suggested retry delay in seconds; longer account windows may still apply."
          }
        }
      },
      "Error500": {
        "description": "Internal failure; details are not exposed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error503": {
        "description": "Password hashing capacity exhausted, or email service is not configured",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Suggested retry delay in seconds; longer account windows may still apply."
          }
        }
      }
    }
  }
}
