{
  "openapi": "3.1.0",
  "info": {
    "title": "Zetapad app integration API",
    "version": "1.4.0",
    "description": "Backend authentication is required for every app, even with zero scopes. Version 2 iframe replies contain only code, expiresIn, and state; identity and profile fields require REST userinfo. Keep secrets and tokens on your backend. Codes are single-use and expire after 60 seconds; tokens expire after 900 seconds. No refresh tokens are issued."
  },
  "servers": [
    {
      "url": "https://zetapad.com"
    }
  ],
  "externalDocs": {
    "url": "https://zetapad.com/developers/docs/api-reference"
  },
  "paths": {
    "/v1/apps/calls/status": {
      "get": {
        "operationId": "readCurrentCallStatus",
        "summary": "Silently check owning-tab call availability before per-call approval",
        "description": "Requires calls:read, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "responses": {
          "200": {
            "description": "Minimal availability; approved means remembered permission, not an active grant or presentation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallStatus"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/attach": {
      "post": {
        "operationId": "attachCurrentCall",
        "summary": "Request native approval for this launch's accepted call",
        "description": "Requires calls:read, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/start": {
      "post": {
        "operationId": "requestCallStart",
        "summary": "Open native call invitation controls",
        "description": "Requires calls:control, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallOperation"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/accept": {
      "post": {
        "operationId": "requestCallAcceptance",
        "summary": "Request native acceptance of the current invitation",
        "description": "Requires calls:control, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallOperation"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/grants/{id}": {
      "get": {
        "operationId": "readCallGrant",
        "summary": "Read approval status, public roster, and native media status",
        "description": "Requires calls:read, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/grants/{id}/commands/{kind}": {
      "post": {
        "operationId": "requestCallCommand",
        "summary": "Queue invite, action, playback, or activityInvite",
        "description": "Requires calls:control, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallOperation"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/commands/{id}": {
      "get": {
        "operationId": "readCallCommand",
        "summary": "Read native command outcome",
        "description": "Requires calls:control, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/grants/{id}/presentations": {
      "post": {
        "operationId": "prepareCallPresentation",
        "summary": "Prepare a video or audioOnly presentation",
        "description": "Requires calls:read, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallPrepare"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/presentations/{id}/tiles": {
      "post": {
        "operationId": "createHostedCallTile",
        "summary": "Create a one-use current-speaker iframe URL",
        "description": "Requires calls:read, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallTile"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/presentations/{id}/{operation}": {
      "post": {
        "operationId": "updateCallPresentation",
        "summary": "Request commit, heartbeat, or release",
        "description": "Requires calls:read, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "operation",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/activities": {
      "post": {
        "operationId": "registerCallActivity",
        "summary": "Register an app backend session in this call",
        "description": "Requires calls:control, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallActivity"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/activities/{id}": {
      "get": {
        "operationId": "readCallActivity",
        "summary": "Read this app's activity label and call",
        "description": "Requires calls:control, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/calls/activity-entry": {
      "get": {
        "operationId": "readVerifiedActivityEntry",
        "summary": "Read server-verified current activity context",
        "description": "Requires calls:read, completed backend login, and the current mounted launch. Call control also requires calls:read. Status needs no per-call approval and creates no grant or prompt; it reports only accepted participation in the owning tab and is advisory. Start and accept open native confirmation controls. Roster, presentation, and grant-bound actions require per-call approval. All responses use private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current status or queued request; committing requires trusted native tile readiness."
          },
          "400": {
            "description": "Unsupported operation or invalid input."
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Call changed or a seat is unavailable."
          },
          "429": {
            "description": "Request or invitation rate limit."
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/token": {
      "post": {
        "operationId": "exchangeAppCode",
        "summary": "Exchange an iframe authorization code",
        "description": "Validate session-bound state before exchange. The code must belong to the authenticated app. Responses use Cache-Control: private, no-store.",
        "security": [
          {
            "clientCredentials": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Opaque app access token; retain on the server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            }
          },
          "400": {
            "description": "BAD_USER_INPUT: send a JSON object containing only code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/userinfo": {
      "get": {
        "operationId": "readAppMember",
        "summary": "Read currently approved member fields",
        "description": "Checks current app access and consent on every call. Use before protected app actions. Responses use Cache-Control: private, no-store.",
        "security": [
          {
            "appAccess": []
          }
        ],
        "responses": {
          "200": {
            "description": "Shared server userId, scopes, and only approved member fields. Identity is included even with no scopes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "401": {
            "description": "INVALID_CLIENT or INVALID_TOKEN: credentials, code, or token cannot be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_REVOKED or APP_UNAVAILABLE: consent or issued app access ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "INTERNAL_SERVER_ERROR: app services are temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "clientCredentials": {
        "type": "http",
        "scheme": "basic",
        "description": "Base64-encoded clientId:clientSecret."
      },
      "appAccess": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque",
        "description": "Server-held token from /v1/apps/token."
      }
    },
    "schemas": {
      "Scope": {
        "type": "string",
        "enum": [
          "profile:read",
          "birthdate:read",
          "calls:read",
          "calls:control"
        ]
      },
      "Token": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "scopes"
        ],
        "properties": {
          "access_token": {
            "type": "string",
            "minLength": 1
          },
          "token_type": {
            "type": "string",
            "const": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "const": 900
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "uniqueItems": true
          }
        }
      },
      "Member": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "userId",
          "scopes"
        ],
        "description": "The shared server userId is the sole member identity and is included even with no scopes. Unapproved profile fields are omitted.",
        "properties": {
          "userId": {
            "type": "string",
            "minLength": 1,
            "description": "Server-generated primary member ID shared across apps, never a Firebase UID; no profile permission required."
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "uniqueItems": true
          },
          "username": {
            "type": "string",
            "minLength": 3,
            "maxLength": 30,
            "pattern": "^[a-z0-9_]{3,30}$",
            "description": "Included with profile:read, even before profile completion. Unique current handle; members can change it anytime. Use userId for permanent records."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Included with profile:read."
          },
          "countryCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Included with profile:read."
          },
          "birthdate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Included with birthdate:read; YYYY-MM-DD."
          }
        }
      },
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "CallStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "inCall",
          "canAttach",
          "access"
        ],
        "description": "Accepted participation in the authenticated launch's owning tab only. No call IDs, roster, grant, prompt, or media. Attach rechecks authorization.",
        "properties": {
          "inCall": {
            "type": "boolean"
          },
          "canAttach": {
            "type": "boolean"
          },
          "access": {
            "type": "string",
            "enum": [
              "unavailable",
              "approvalRequired",
              "approved"
            ]
          }
        },
        "oneOf": [
          {
            "properties": {
              "inCall": {
                "const": false
              },
              "canAttach": {
                "const": false
              },
              "access": {
                "const": "unavailable"
              }
            }
          },
          {
            "properties": {
              "inCall": {
                "const": true
              },
              "canAttach": {
                "const": true
              },
              "access": {
                "enum": [
                  "approvalRequired",
                  "approved"
                ]
              }
            }
          }
        ]
      },
      "CallOperation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "clientId"
        ],
        "properties": {
          "clientId": {
            "type": "string",
            "minLength": 16,
            "maxLength": 128,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "action": {
            "type": "string",
            "enum": [
              "cameraOn",
              "cameraOff",
              "unmute",
              "mute",
              "leave",
              "end",
              "stage/request",
              "stage/approve",
              "stage/reject",
              "stage/join",
              "stage/cancel",
              "stage/leave",
              "stage/demote",
              "decline",
              "cancel"
            ]
          },
          "targetId": {
            "type": "string",
            "maxLength": 128
          },
          "recipientId": {
            "type": "string",
            "maxLength": 128
          },
          "role": {
            "type": "string",
            "enum": [
              "speaker",
              "audience"
            ]
          },
          "muted": {
            "type": "boolean"
          },
          "activityId": {
            "type": "string",
            "maxLength": 128
          }
        }
      },
      "CallParticipant": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "userId",
          "participationId",
          "username",
          "avatarUrl",
          "role",
          "presence",
          "stageState"
        ],
        "properties": {
          "userId": {
            "type": "string"
          },
          "participationId": {
            "type": "string"
          },
          "username": {
            "type": "string"
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "speaker",
              "audience"
            ]
          },
          "presence": {
            "type": "string",
            "enum": [
              "connected",
              "reconnecting",
              "leaving"
            ]
          },
          "stageState": {
            "type": "string",
            "enum": [
              "none",
              "requested",
              "approved"
            ]
          }
        }
      },
      "CallPrepare": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "video",
              "audioOnly"
            ]
          }
        }
      },
      "CallTile": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "participationId",
          "fit",
          "label"
        ],
        "properties": {
          "participationId": {
            "type": "string",
            "maxLength": 128
          },
          "fit": {
            "type": "string",
            "enum": [
              "cover",
              "contain"
            ]
          },
          "label": {
            "type": "boolean"
          }
        }
      },
      "CallActivity": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "grantId",
          "sessionKey",
          "label"
        ],
        "properties": {
          "grantId": {
            "type": "string"
          },
          "sessionKey": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          }
        }
      }
    }
  }
}
