{
  "openapi": "3.1.0",
  "info": {
    "title": "Whiteline Phone Agent Cloud Gateway API",
    "version": "0.5.0",
    "summary": "Autonomous Android control over AccessibilityService, Streamable HTTP MCP, and Gemini vision planner.",
    "description": "Open-Core BYO-Gateway HTTP REST and Model Context Protocol (MCP) interface for observing Android UI trees, capturing JPEG screenshots, resolving compact @e1..@eN element references with optimistic screen_hash locks, and orchestrating missions.",
    "contact": {
      "name": "Whiteline Phone Agent",
      "url": "https://phonectl.dev/"
    },
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://mcp.phonectl.dev",
      "description": "Personal BYO-Gateway custom domain endpoint"
    },
    {
      "url": "http://127.0.0.1:8765",
      "description": "Local loopback development gateway"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/v1/health": {
      "get": {
        "operationId": "getGatewayHealth",
        "summary": "Unauthenticated gateway health and device connectivity status",
        "security": [],
        "responses": {
          "200": {
            "description": "Gateway health metrics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpec",
        "summary": "Retrieve OpenAPI 3.1.0 JSON schema for this gateway",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1.0 document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "postMcpJsonRpc",
        "summary": "Streamable HTTP Model Context Protocol JSON-RPC 2.0 endpoint",
        "description": "Handles MCP methods initialize, ping, tools/list, tools/call (phone_status, phone_observe, phone_act, phone_run, phone_apps, phone_cancel), resources/list, and resources/read.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted JSON-RPC notification with empty body"
          },
          "401": {
            "description": "Missing or invalid Bearer token"
          }
        }
      }
    },
    "/api/v1/device/screen": {
      "get": {
        "operationId": "getDeviceScreen",
        "summary": "Get latest cached device screenshot, UI tree, and installed apps",
        "parameters": [
          {
            "name": "include_screenshot",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "include_tree",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "include_apps",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Latest device snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceScreenResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/v1/device/poll": {
      "get": {
        "operationId": "pollPendingMission",
        "summary": "Long-poll from Android device agent for the next pending mission",
        "parameters": [
          {
            "name": "wait_sec",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Next claimed mission or empty poll",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/v1/missions": {
      "get": {
        "operationId": "listMissions",
        "summary": "List recent device missions",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mission list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createMission",
        "summary": "Queue a natural-language goal or deterministic directive mission",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMissionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created mission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Mission"
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/v1/missions/{id}": {
      "get": {
        "operationId": "getMission",
        "summary": "Retrieve mission status and recorded execution events",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mission record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Mission"
                }
              }
            }
          },
          "404": {
            "description": "Mission not found"
          }
        }
      }
    },
    "/api/v1/missions/{id}/screen": {
      "get": {
        "operationId": "getMissionScreen",
        "summary": "Retrieve latest captured screen snapshot scoped to a mission",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_screenshot",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "include_tree",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mission snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Mission not found"
          }
        }
      }
    },
    "/api/v1/missions/{id}/events": {
      "post": {
        "operationId": "postMissionEvent",
        "summary": "Report execution result for one step from Android device",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MissionEventRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recorded"
          },
          "404": {
            "description": "Mission not found"
          }
        }
      }
    },
    "/api/v1/missions/{id}/cancel": {
      "post": {
        "operationId": "cancelMission",
        "summary": "Abort an active or pending mission immediately",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelMissionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Aborted mission state"
          },
          "404": {
            "description": "Mission not found"
          }
        }
      }
    },
    "/api/v1/plan-step": {
      "post": {
        "operationId": "planStep",
        "summary": "Plan the next Android accessibility action or evaluate direct directives",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanStepRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Next validated Android action"
          },
          "400": {
            "description": "Invalid payload"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Shared secret token configured via APC_GATEWAY_TOKEN on Railway."
      }
    },
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "planner_provider": {
            "type": "string"
          },
          "planner_model": {
            "type": "string"
          },
          "has_llm_key": {
            "type": "boolean"
          },
          "last_device_poll_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_device_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "installed_apps_count": {
            "type": "integer"
          }
        }
      },
      "CreateMissionRequest": {
        "type": "object",
        "required": [
          "prompt"
        ],
        "properties": {
          "prompt": {
            "type": "string"
          },
          "max_steps": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "default": 25
          }
        }
      },
      "Mission": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "prompt": {
            "type": "string"
          },
          "max_steps": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "RUNNING",
              "COMPLETED",
              "FAILED",
              "ABORTED"
            ]
          },
          "cancel_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "result_summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "DeviceScreenResponse": {
        "type": "object",
        "properties": {
          "has_snapshot": {
            "type": "boolean"
          },
          "device_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_mission_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_screenshot": {
            "type": "boolean"
          },
          "screenshot_b64": {
            "type": [
              "string",
              "null"
            ]
          },
          "ui_tree": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "installed_apps": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "installed_apps_count": {
            "type": "integer"
          }
        }
      },
      "MissionEventRequest": {
        "type": "object",
        "required": [
          "step",
          "action",
          "status"
        ],
        "properties": {
          "step": {
            "type": "integer",
            "minimum": 0
          },
          "action": {
            "type": "object"
          },
          "status": {
            "type": "string"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CancelMissionRequest": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string"
          }
        }
      },
      "PlanStepRequest": {
        "type": "object",
        "required": [
          "prompt"
        ],
        "properties": {
          "prompt": {
            "type": "string"
          },
          "mission_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "ui_tree": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "screenshot_b64": {
            "type": [
              "string",
              "null"
            ]
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "installed_apps": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "method": {
            "type": "string"
          },
          "params": {
            "type": "object"
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "result": {
            "type": "object"
          },
          "error": {
            "type": "object"
          }
        }
      }
    }
  }
}
