{
  "openapi": "3.1.0",
  "info": {
    "title": "Wordy Page Publishing, MCP & Site Management API",
    "version": "1.2.0",
    "summary": "Passwordless 60-second web page and multi-page site builder for humans and AI agents.",
    "description": "Programmatic JSON & MCP API for checking slug availability, authenticating via passwordless email magic links, searching Unsplash photography (one header image per page via `photo`), and publishing single-page or multi-page websites on https://wordy.page. Formatting rules: contiguous ALL CAPS sequences (2+ uppercase letters) render in blue highlight color (do NOT use **bold** asterisks); leading spaces and tabs (`\\t`) are preserved as indentation; those are the ONLY formatting. Newlines are line breaks and blank lines stay blank; HTML/code is safely escaped as plain text; [anchor text](https://...) and plain URLs/emails render dofollow links. Remote MCP server available at https://wordy.page/mcp. If your HTTP client receives an anti-bot HTML challenge on its first request, include `Cookie: humans_21909=1`.",
    "contact": {
      "name": "Wordy Page",
      "url": "https://wordy.page/contact-us"
    }
  },
  "servers": [
    {
      "url": "https://wordy.page",
      "description": "Production Wordy Page server"
    }
  ],
  "paths": {
    "/mcp": {
      "get": {
        "operationId": "getMcpDiscovery",
        "summary": "Retrieve Model Context Protocol (MCP) server discovery metadata",
        "responses": {
          "200": {
            "description": "MCP server metadata and tool descriptors (`mcp.json`)"
          }
        }
      },
      "post": {
        "operationId": "invokeMcpJsonRpc",
        "summary": "Streamable HTTP JSON-RPC 2.0 endpoint for Model Context Protocol (MCP) clients",
        "description": "Supports MCP JSON-RPC 2.0 methods (`initialize`, `ping`, `tools/list`, `tools/call`, `resources/list`, `resources/read`, `prompts/list`, `prompts/get`) and tools (`check_slug`, `get_page`, `list_pages`, `search_photos`, `create_or_update_page`).",
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response"
          }
        }
      }
    },
    "/publish.php": {
      "get": {
        "operationId": "queryPublishStatus",
        "summary": "Check slug availability, current signed-in session, or page ownership",
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "check",
                "me",
                "owns"
              ]
            },
            "description": "`check` tests if `slug` is valid and available; `me` returns the signed-in email; `owns` checks if the current session owns `slug`."
          },
          {
            "name": "slug",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON status object",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "valid": {
                      "type": "boolean"
                    },
                    "available": {
                      "type": "boolean"
                    },
                    "mine": {
                      "type": "boolean"
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "executePublishAction",
        "summary": "Authenticate, publish, update, list, or delete sites on Wordy Page",
        "description": "Supports actions: `send_link`, `link`, `publish`, `list`, `get`, `unpublish`, `delete_account`, `account`, and `signout`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "send_link",
                      "link",
                      "publish",
                      "list",
                      "get",
                      "unpublish",
                      "delete_account",
                      "account",
                      "signout"
                    ]
                  },
                  "st": {
                    "type": "string",
                    "description": "Session token returned by `action: link` (can also be sent in the `X-PQ-Session` request header)."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email address for `send_link` or `account` actions."
                  },
                  "token": {
                    "type": "string",
                    "description": "32-character one-time login token from emailed link (`https://wordy.page/pages?login=<token>`) for `action: link`."
                  },
                  "slug": {
                    "type": "string",
                    "description": "Target site slug (3-40 lowercase alphanumeric/hyphen characters; auto-normalized from spaces/punctuation)."
                  },
                  "from": {
                    "type": "string",
                    "description": "Optional existing slug owned by the user to rename/move from when publishing."
                  },
                  "owner_email": {
                    "type": "string",
                    "format": "email",
                    "description": "Owner email address when calling `action: publish`."
                  },
                  "confirm": {
                    "type": "string",
                    "description": "Pass `\"delete\"` when calling `action: delete_account`."
                  },
                  "data": {
                    "type": "object",
                    "description": "Site content and appearance payload for `action: publish`.",
                    "properties": {
                      "title": {
                        "type": "string",
                        "maxLength": 36,
                        "description": "Site name shown in the sticky top bar (up to 36 characters; stacks up to 2 lines on mobile without changing header height, and links back to the first home page)."
                      },
                      "heading": {
                        "type": "string",
                        "maxLength": 80,
                        "description": "Home page H1 heading and navigation menu label."
                      },
                      "description": {
                        "type": "string",
                        "maxLength": 20000,
                        "description": "Home page body text. Formatting: contiguous ALL CAPS sequences (2+ uppercase letters) are highlighted in blue (`<strong>` with normal font weight; do NOT use `**bold**` asterisks); leading spaces and tabs (`\\t`) are preserved as indentation (the only other formatting); newlines are line breaks and blank lines stay blank; HTML/code is safely escaped as plain text; [anchor text](https://...) and plain URLs/emails render dofollow links. Images go in `photo`, not in text."
                      },
                      "theme": {
                        "type": "string",
                        "enum": [
                          "light",
                          "dark"
                        ]
                      },
                      "sections": {
                        "type": "array",
                        "maxItems": 9,
                        "description": "Up to 9 additional sub-pages (home + 9 = 10 pages total), each rendered at `/<slug>/<section-slug>/`.",
                        "items": {
                          "type": "object",
                          "required": [
                            "heading",
                            "text"
                          ],
                          "properties": {
                            "heading": {
                              "type": "string",
                              "maxLength": 80,
                              "description": "Sub-page H1 heading and menu label."
                            },
                            "text": {
                              "type": "string",
                              "maxLength": 20000,
                              "description": "Sub-page body text. Uses the same formatting rules as `description` (only ALL CAPS blue highlights and preserved spaces/tabs; plain-text HTML escaping and auto-linked URLs/emails)."
                            },
                            "photo": {
                              "type": "string",
                              "description": "Optional single header image for this page (Unsplash photo id)."
                            },
                            "cf": {
                              "type": "boolean",
                              "default": false,
                              "description": "Add a contact form (Email and Message only) at the bottom of this page."
                            }
                          }
                        }
                      },
                      "photo": {
                        "type": "string",
                        "description": "Optional single header image for the home page (Unsplash photo id). Renders below the page name, above the text."
                      },
                      "cf": {
                        "type": "boolean",
                        "default": false,
                        "description": "Add a contact form (Email and Message only) at the bottom of the home page."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Action result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "need_link": {
                      "type": "boolean"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "paths": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "token": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/unsplash.php": {
      "get": {
        "operationId": "searchOrGetUnsplashPhotos",
        "summary": "Search Unsplash photos (`?q=...`) or fetch cached metadata by IDs (`?ids=id1,id2`)",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Search query keywords."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "ids",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated list of Unsplash photo IDs (up to 30) to look up cached thumbnail/attribution metadata."
          }
        ],
        "responses": {
          "200": {
            "description": "Search results or photo metadata map with IDs, thumbnails, and photographer attribution."
          }
        }
      },
      "post": {
        "operationId": "pickUnsplashPhoto",
        "summary": "Register an Unsplash photo ID before publishing it in a page",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Unsplash photo ID from search results"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Confirmation; use the id as a page `photo`"
          }
        }
      }
    }
  }
}
