{
  "openapi": "3.1.0",
  "info": {
    "title": "SubmitDock Pages API",
    "version": "1.0.0",
    "description": "API for AI agents and developers to create, update, and manage public support and privacy policy pages for iOS apps submitted to App Store Connect. Supports Markdown formatting and image uploads (Base64 JSON or multipart/form-data)."
  },
  "servers": [
    {
      "url": "/",
      "description": "Current host"
    }
  ],
  "paths": {
    "/api/pages": {
      "post": {
        "summary": "Create a new app page pair (Support & Privacy Policy)",
        "description": "Creates persistent support and privacy policy pages. Returns public URLs and a one-time editToken. Accepts either application/json (with base64 images) or multipart/form-data.",
        "operationId": "createPage",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePageJsonRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreatePageMultipartRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Page created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input or validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/pages/{id}": {
      "get": {
        "summary": "Get page public details",
        "description": "Retrieves public details of the page, including URLs, Markdown content, and screenshot image URLs.",
        "operationId": "getPage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique page ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Page record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageRecord"
                }
              }
            }
          },
          "404": {
            "description": "Page not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Partially update page content",
        "description": "Updates only specified fields. Requires Authorization: Bearer <editToken> header.",
        "operationId": "patchPage",
        "security": [
          {
            "EditTokenAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePageJsonRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreatePageMultipartRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Page updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageRecord"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing edit token"
          },
          "404": {
            "description": "Page not found"
          }
        }
      },
      "put": {
        "summary": "Replace full page content",
        "description": "Replaces all content and replaces/clears screenshots. Requires Authorization: Bearer <editToken>.",
        "operationId": "putPage",
        "security": [
          {
            "EditTokenAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePageJsonRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Page replaced",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageRecord"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing edit token"
          }
        }
      },
      "delete": {
        "summary": "Delete page and all uploaded images",
        "description": "Permanently deletes the page and stored screenshots. Requires Authorization: Bearer <editToken>.",
        "operationId": "deletePage",
        "security": [
          {
            "EditTokenAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing edit token"
          }
        }
      }
    },
    "/api/pages/{id}/token": {
      "post": {
        "summary": "Issue or claim editToken for an existing page",
        "description": "Issue or rotate using admin authorization or the current edit token. Public email is not proof of ownership.",
        "operationId": "issuePageToken",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Existing page ID"
          },
          {
            "name": "X-Admin-Password",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional admin master password to issue token for any page"
          }
        ],
        "responses": {
          "200": {
            "description": "Token issued successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssueTokenResponse"
                }
              }
            }
          },
          "401": {
            "description": "Admin authorization or current edit token required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Page not found"
          },
          "409": {
            "description": "Page already has an active edit token. Admin password or current token is required."
          }
        }
      }
    },
    "/admin/api/releases": {
      "get": {
        "summary": "Private registry list",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        }
      }
    },
    "/admin/api/releases/{id}": {
      "get": {
        "summary": "Private release record",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      },
      "put": {
        "summary": "Create/replace a whole manifest with expectedRevision CAS",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/admin/api/releases/{id}/history": {
      "get": {
        "summary": "Immutable prior drafts",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/admin/api/releases/{id}/diff": {
      "post": {
        "summary": "Preview field differences",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/admin/api/releases/{id}/restore": {
      "post": {
        "summary": "Restore draft as a new revision; preserve review observations",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/admin/api/releases/{id}/publish": {
      "post": {
        "summary": "Apply the draft to the existing public page; X-Release-Revision required",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/admin/api/app-ads": {
      "post": {
        "summary": "Append and deduplicate one official AdMob DIRECT line; preserve existing lines",
        "security": [
          {
            "AdminBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Admin authentication required"
          },
          "409": {
            "description": "Revision conflict"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "EditTokenAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "The secret editToken returned when the page was created."
      },
      "AdminBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Existing SubmitDock admin password, stored in the local environment. Never in manifest."
      }
    },
    "schemas": {
      "Base64Image": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Original filename (e.g. screenshot.png)"
          },
          "contentType": {
            "type": "string",
            "description": "MIME type (image/png, image/jpeg, image/webp)"
          },
          "data": {
            "type": "string",
            "description": "Base64 encoded image string, or a Data URI (data:image/png;base64,...)"
          }
        }
      },
      "CreatePageJsonRequest": {
        "type": "object",
        "required": [
          "appName",
          "developerName",
          "supportEmail",
          "appDescription",
          "privacyPolicy"
        ],
        "properties": {
          "appName": {
            "type": "string",
            "maxLength": 120,
            "example": "Habit Tracker Pro"
          },
          "developerName": {
            "type": "string",
            "maxLength": 120,
            "example": "Studio Hino"
          },
          "supportEmail": {
            "type": "string",
            "format": "email",
            "maxLength": 160,
            "example": "support@example.com"
          },
          "appStoreUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 300,
            "description": "Optional App Store link",
            "example": "https://apps.apple.com/app/id123456789"
          },
          "appDescription": {
            "type": "string",
            "maxLength": 4000,
            "description": "Markdown formatted description of the app"
          },
          "supportContent": {
            "type": "string",
            "maxLength": 20000,
            "description": "Optional Markdown for FAQ, troubleshooting, guide"
          },
          "privacyPolicy": {
            "type": "string",
            "maxLength": 40000,
            "description": "Markdown formatted privacy policy body"
          },
          "appIcon": {
            "$ref": "#/components/schemas/Base64Image",
            "description": "Optional square app icon (up to 1MB)"
          },
          "screenshots": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "$ref": "#/components/schemas/Base64Image"
            },
            "description": "Up to 6 screenshots. Total image bytes up to 4MB (JSON) or 5.5MB (multipart)."
          }
        }
      },
      "UpdatePageJsonRequest": {
        "type": "object",
        "properties": {
          "appName": {
            "type": "string",
            "maxLength": 120
          },
          "developerName": {
            "type": "string",
            "maxLength": 120
          },
          "supportEmail": {
            "type": "string",
            "format": "email",
            "maxLength": 160
          },
          "appStoreUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 300
          },
          "appDescription": {
            "type": "string",
            "maxLength": 4000
          },
          "supportContent": {
            "type": "string",
            "maxLength": 20000
          },
          "privacyPolicy": {
            "type": "string",
            "maxLength": 40000
          },
          "appIcon": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Base64Image"
              },
              {
                "type": "null",
                "description": "Pass null to remove icon"
              }
            ]
          },
          "screenshots": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "$ref": "#/components/schemas/Base64Image"
            },
            "description": "Array of screenshots to replace current ones. Pass [] or null to clear."
          }
        }
      },
      "CreatePageMultipartRequest": {
        "type": "object",
        "required": [
          "appName",
          "developerName",
          "supportEmail",
          "appDescription",
          "privacyPolicy"
        ],
        "properties": {
          "appName": {
            "type": "string"
          },
          "developerName": {
            "type": "string"
          },
          "supportEmail": {
            "type": "string"
          },
          "appStoreUrl": {
            "type": "string"
          },
          "appDescription": {
            "type": "string"
          },
          "supportContent": {
            "type": "string"
          },
          "privacyPolicy": {
            "type": "string"
          },
          "appIcon": {
            "type": "string",
            "format": "binary"
          },
          "screenshots": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            }
          },
          "clearScreenshots": {
            "type": "string",
            "enum": [
              "true",
              "false"
            ]
          },
          "removeAppIcon": {
            "type": "string",
            "enum": [
              "true",
              "false"
            ]
          }
        }
      },
      "PageRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "appName": {
            "type": "string"
          },
          "developerName": {
            "type": "string"
          },
          "supportEmail": {
            "type": "string"
          },
          "appStoreUrl": {
            "type": "string"
          },
          "appDescription": {
            "type": "string"
          },
          "supportContent": {
            "type": "string"
          },
          "privacyPolicy": {
            "type": "string"
          },
          "appIcon": {
            "type": "object",
            "nullable": true,
            "properties": {
              "url": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              }
            }
          },
          "screenshots": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "contentType": {
                  "type": "string"
                }
              }
            }
          },
          "contentFormat": {
            "type": "string",
            "example": "markdown"
          },
          "editable": {
            "type": "boolean"
          },
          "supportUrl": {
            "type": "string"
          },
          "privacyUrl": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreatePageResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "editToken": {
            "type": "string",
            "description": "Secret token to update/delete page later"
          },
          "supportUrl": {
            "type": "string"
          },
          "privacyUrl": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "page": {
            "$ref": "#/components/schemas/PageRecord"
          }
        }
      },
      "IssueTokenResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "editToken": {
            "type": "string",
            "description": "New active secret token for editing/deleting"
          },
          "supportUrl": {
            "type": "string"
          },
          "privacyUrl": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "page": {
            "$ref": "#/components/schemas/PageRecord"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    }
  }
}
