{
  "openapi": "3.1.0",
  "info": {
    "title": "WebsitePublisher.ai API",
    "description": "Build and publish websites with pages, assets, dynamic content, integrations, forms, visitor auth, and scheduling.",
    "version": "1.7.0"
  },
  "servers": [
    {
      "url": "https://api.websitepublisher.ai",
      "description": "WebsitePublisher.ai API"
    }
  ],
  "components": {
    "securitySchemes": {
      "oauth": {
        "type": "oauth2",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://api.websitepublisher.ai/dapi/oauth/authorize",
            "tokenUrl": "https://api.websitepublisher.ai/dapi/oauth/token",
            "scopes": {
              "read": "Read access to pages, assets, entities",
              "write": "Write access to pages, assets, entities"
            }
          }
        }
      }
    },
    "schemas": {
      "Page": {
        "type": "object",
        "required": [
          "slug",
          "content"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "URL path like about.htm",
            "example": "about.htm"
          },
          "content": {
            "type": "string",
            "description": "Complete HTML with DOCTYPE"
          },
          "meta": {
            "$ref": "#/components/schemas/PageMeta"
          },
          "seo": {
            "$ref": "#/components/schemas/PageSeo"
          }
        }
      },
      "PageMeta": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "landingpage": {
            "type": "boolean",
            "description": "True for homepage"
          },
          "language": {
            "type": "string",
            "example": "en"
          }
        }
      },
      "PageSeo": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "keywords": {
            "type": "string"
          }
        }
      },
      "PageUpdate": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string"
          },
          "meta": {
            "$ref": "#/components/schemas/PageMeta"
          },
          "seo": {
            "$ref": "#/components/schemas/PageSeo"
          },
          "base_version_hash": {
            "type": "string",
            "description": "Version hash from getPage. Required when updating content to prevent accidental overwrites. Omit only with force: true.",
            "example": "2f025efc"
          },
          "force": {
            "type": "boolean",
            "description": "Skip version hash check and overwrite regardless of changes by others."
          }
        }
      },
      "AssetCreate": {
        "type": "object",
        "required": [
          "slug"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Desired file path for the asset. Used for upload only — use the returned url field in your HTML.",
            "example": "images/logo.png"
          },
          "content": {
            "type": "string",
            "description": "Base64 encoded file content (no data: prefix). Use this OR content_text OR source_url — exactly one."
          },
          "content_text": {
            "type": "string",
            "description": "Plain text content for text-based assets (JS, JSX, TS, TSX, CSS, JSON, SVG, TXT, HTML, XML, MD, CSV). Preferred over content (base64) for text files — saves ~33% tokens. Use this OR content OR source_url — exactly one."
          },
          "source_url": {
            "type": "string",
            "format": "uri",
            "description": "Public HTTPS URL to fetch the file from (e.g. DALL-E image URL, Unsplash). Use this OR content, not both. Supports images, PDF, fonts, ico. For CSS/SVG/JSON use content instead.",
            "example": "https://images.unsplash.com/photo-example.jpg"
          },
          "alt": {
            "type": "string",
            "description": "Alt text for images"
          }
        }
      },
      "AssetResponse": {
        "type": "object",
        "description": "Asset metadata returned by the API. Use the url field in your HTML src attributes.",
        "properties": {
          "asset_id": {
            "type": "string",
            "description": "Unique identifier for this asset. Use for DELETE and GET operations.",
            "example": "images/logo.png"
          },
          "url": {
            "type": "string",
            "description": "Full CDN URL — use this in HTML src attributes.",
            "example": "https://cdn.websitepublisher.ai/custom/wid22253/images/logo.png"
          },
          "mime_type": {
            "type": "string",
            "example": "image/png"
          },
          "size": {
            "type": "integer",
            "description": "File size in bytes"
          },
          "alt": {
            "type": "string",
            "description": "Alt text for images"
          }
        }
      },
      "Entity": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "blogpost"
          },
          "plural": {
            "type": "string",
            "example": "blogposts"
          },
          "description": {
            "type": "string"
          },
          "public_read": {
            "type": "boolean",
            "description": "Enable public read-only access without authentication (default: false)",
            "default": false
          },
          "properties": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Property"
            }
          }
        }
      },
      "Property": {
        "type": "object",
        "required": [
          "name",
          "type"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "title"
          },
          "type": {
            "type": "string",
            "enum": [
              "varchar",
              "int",
              "text",
              "datetime",
              "tinyint"
            ]
          },
          "length": {
            "type": "integer",
            "example": 255
          },
          "required": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "GenericRecord": {
        "type": "object",
        "required": [
          "record_json"
        ],
        "properties": {
          "record_json": {
            "type": "string",
            "description": "JSON-encoded record object. Keys must be field names from getEntitySchema. Example: {\"title\": \"My Post\", \"content\": \"Hello world\", \"status\": \"published\"}"
          }
        }
      },
      "EntityUpdate": {
        "type": "object",
        "description": "Update entity metadata. At least one field required.",
        "properties": {
          "plural": {
            "type": "string",
            "description": "New plural form of entity name",
            "example": "blogposts"
          },
          "description": {
            "type": "string",
            "description": "New description"
          },
          "public_read": {
            "type": "boolean",
            "description": "Enable public read-only access without authentication (default: false)"
          }
        }
      },
      "PatchOperation": {
        "type": "object",
        "required": [
          "operation",
          "find"
        ],
        "properties": {
          "operation": {
            "type": "string",
            "enum": [
              "replace",
              "delete"
            ],
            "description": "Operation type: 'replace' swaps text, 'delete' removes it"
          },
          "find": {
            "type": "string",
            "description": "Exact string to find in the page content (must match exactly once)"
          },
          "replace": {
            "type": "string",
            "description": "Replacement string (required for replace operation, omit for delete)"
          }
        }
      },
      "PagePatch": {
        "type": "object",
        "required": [
          "patches"
        ],
        "properties": {
          "base_version_hash": {
            "type": "string",
            "description": "Optional. Version hash from getPage response. If provided and mismatched, returns 409. If omitted, find/replace handles conflicts naturally.",
            "example": "6db1457e"
          },
          "patches": {
            "type": "array",
            "description": "Array of patch operations to apply sequentially",
            "items": {
              "$ref": "#/components/schemas/PatchOperation"
            }
          },
          "patch_summary": {
            "type": "string",
            "description": "Brief description of what was changed (stored in version history)",
            "example": "Updated hero section heading"
          }
        }
      },
      "PageRollback": {
        "type": "object",
        "properties": {
          "target_version": {
            "type": "integer",
            "description": "Version number to rollback to (use this OR target_version_hash)",
            "example": 2
          },
          "target_version_hash": {
            "type": "string",
            "description": "Version hash to rollback to (use this OR target_version)",
            "example": "6db1457e"
          }
        }
      },
      "EditSessionRequest": {
        "type": "object",
        "required": [
          "page_slug"
        ],
        "properties": {
          "page_slug": {
            "type": "string",
            "description": "Page slug to edit (e.g., index.htm, about.htm)"
          },
          "capabilities_json": {
            "type": "string",
            "description": "JSON array of capabilities: image_upload, image_resize, image_padding, image_remove. Default: [\"image_upload\",\"image_resize\",\"image_padding\"]"
          },
          "ttl_minutes": {
            "type": "integer",
            "description": "Session duration in minutes (5-120, default: 30)"
          }
        }
      },
      "IntegrationSetup": {
        "type": "object",
        "required": [
          "secrets_json"
        ],
        "properties": {
          "secrets_json": {
            "type": "string",
            "description": "JSON-encoded secrets object. Use listAvailableIntegrations for required keys. Example for Resend: {\"resend_api_key\": \"re_abc123...\"}"
          }
        }
      },
      "IntegrationExecute": {
        "type": "object",
        "required": [
          "input_json"
        ],
        "properties": {
          "input_json": {
            "type": "string",
            "description": "JSON-encoded input. Fields depend on endpoint. Example for Resend send-email: {\"from\": \"noreply@site.com\", \"to\": \"user@example.com\", \"subject\": \"Hello\", \"html\": \"<p>Hi</p>\"}"
          }
        }
      },
      "VaultSecretStore": {
        "type": "object",
        "required": [
          "key_name",
          "value"
        ],
        "properties": {
          "key_name": {
            "type": "string",
            "description": "Unique identifier (lowercase, underscores)",
            "example": "resend_api_key"
          },
          "value": {
            "type": "string",
            "description": "The secret value to encrypt and store"
          },
          "service_type": {
            "type": "string",
            "description": "Service label (e.g. resend, mollie)"
          },
          "description": {
            "type": "string",
            "description": "Human-readable description"
          }
        }
      },
      "FormConfig": {
        "type": "object",
        "required": [
          "form_name",
          "required_fields"
        ],
        "properties": {
          "form_name": {
            "type": "string",
            "description": "Unique form identifier (lowercase a-z, 0-9, underscore, max 64 chars)",
            "example": "contact"
          },
          "required_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Field names the visitor must fill in",
            "example": [
              "name",
              "email",
              "message"
            ]
          },
          "allowed_action_json": {
            "type": "string",
            "description": "JSON-encoded single action. Keys: type (iapi/webhook/none), service, endpoint, input_template ({{fields.xxx}} placeholders). Example: {\"type\":\"iapi\",\"service\":\"resend\",\"endpoint\":\"send-email\",\"input_template\":{\"from\":\"noreply@site.com\",\"to\":\"info@site.com\",\"subject\":\"{{fields.name}}\",\"text\":\"{{fields.message}}\"}}"
          },
          "allowed_actions_json": {
            "type": "string",
            "description": "JSON-encoded array of actions (max 3). Use instead of allowed_action_json when you need multiple actions (e.g. webhook + confirmation email). Actions execute in order. Example: [{\"type\":\"webhook\",\"url\":\"https://hooks.example.com/lead\"},{\"type\":\"iapi\",\"service\":\"resend\",\"endpoint\":\"send-email\",\"input_template\":{\"from\":\"noreply@site.nl\",\"to\":\"{{fields.email}}\",\"subject\":\"Bevestiging\",\"text\":\"Bedankt {{fields.naam}}\"}}]"
          },
          "max_submits_per_session": {
            "type": "integer",
            "description": "Max submits per visitor session (default: 3, max: 100)",
            "default": 3
          }
        }
      },
      "FragmentCreate": {
        "type": "object",
        "required": [
          "name",
          "content"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Fragment name (lowercase, hyphens/slashes allowed)",
            "example": "menu"
          },
          "content": {
            "type": "string",
            "description": "HTML content of the fragment"
          }
        }
      },
      "FragmentUpdate": {
        "type": "object",
        "required": [
          "content"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "New HTML content of the fragment"
          }
        }
      },
      "VisitorAuthConfig": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Enable or disable visitor auth (default: true)"
          },
          "methods": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "link",
                "code"
              ]
            },
            "description": "Auth methods: link=magic link email, code=6-digit code"
          },
          "success_url": {
            "type": "string",
            "description": "Redirect URL after successful verification (e.g. /welkom)"
          },
          "session_ttl_verified": {
            "type": "integer",
            "description": "Verified session duration in seconds (min 3600, max 2592000)"
          },
          "require_name": {
            "type": "boolean",
            "description": "Ask visitor for name during auth (default: false)"
          },
          "allowed_domains": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Email domain whitelist (e.g. [\"bedrijf.nl\"]). Null = all domains allowed.",
            "nullable": true
          }
        }
      },
      "ScheduledTaskCreate": {
        "type": "object",
        "required": [
          "name",
          "action_type",
          "action_payload",
          "schedule"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable task name",
            "example": "Publish blog post Monday 9am"
          },
          "action_type": {
            "type": "string",
            "enum": [
              "publish_page",
              "unpublish_page",
              "webhook"
            ],
            "description": "Action type. publish/unpublish require page_id in payload. webhook requires url in payload."
          },
          "action_payload": {
            "type": "object",
            "description": "Action parameters. publish_page/unpublish_page: {\"page_id\": 123}. webhook: {\"url\": \"https://...\", \"body\": {}}"
          },
          "schedule": {
            "type": "string",
            "description": "Cron expression (e.g. \"0 9 * * 1\" = Monday 9am)",
            "example": "0 9 * * 1"
          },
          "timezone": {
            "type": "string",
            "description": "Timezone (default: Europe/Amsterdam)",
            "example": "Europe/Amsterdam"
          },
          "run_once": {
            "type": "boolean",
            "description": "Run once at next matching time then deactivate (default: false)"
          }
        }
      }
    }
  },
  "security": [
    {
      "oauth": [
        "read",
        "write"
      ]
    }
  ],
  "tags": [
    {
      "name": "Status",
      "description": "Project status"
    },
    {
      "name": "Pages",
      "description": "HTML pages"
    },
    {
      "name": "Assets",
      "description": "Images and files"
    },
    {
      "name": "Entities",
      "description": "Data models"
    },
    {
      "name": "Records",
      "description": "Entity data"
    },
    {
      "name": "Public",
      "description": "Public read-only access (no authentication required)"
    },
    {
      "name": "Vault",
      "description": "Secure credential storage (encrypted, write-only)"
    },
    {
      "name": "Integrations",
      "description": "Third-party API integrations (email, payments)"
    },
    {
      "name": "Visitor Auth",
      "description": "Email-based visitor authentication (magic link or code)"
    },
    {
      "name": "Scheduling",
      "description": "Automated scheduled tasks (publish, webhook, etc.)"
    },
    {
      "name": "Fragments",
      "description": "Reusable HTML fragments"
    },
    {
      "name": "Forms",
      "description": "Website form configuration"
    },
    {
      "name": "Visual Editor",
      "description": "Browser-based image editor sessions"
    },
    {
      "name": "Skill",
      "description": "Agent skill document"
    }
  ],
  "paths": {
    "/papi/skill": {
      "get": {
        "operationId": "getSkill",
        "summary": "Get a WebsitePublisher agent skill document",
        "description": "ALWAYS call this at the start of every conversation where you will build or modify a website. Call with skill_name=design BEFORE building any HTML pages — it contains typography, color, layout, and animation guidelines that produce professional-quality websites.",
        "tags": [
          "Skill"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "skill_name",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "main",
                "design",
                "dev"
              ],
              "default": "main"
            },
            "description": "Which skill to fetch. main (default): building instructions and SAPI patterns. design: typography, color, layout, animation guidelines — fetch before building HTML. dev: internal platform development."
          }
        ],
        "responses": {
          "200": {
            "description": "Agent skill document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "skill_name": {
                          "type": "string",
                          "description": "Which skill was returned"
                        },
                        "skill": {
                          "type": "string",
                          "description": "Full skill document in Markdown"
                        },
                        "source": {
                          "type": "string",
                          "description": "local or fallback"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/papi/project/{projectId}/status": {
      "get": {
        "summary": "Get project status",
        "description": "Get website status. Use the project_id from your OAuth token response.",
        "operationId": "getProjectStatus",
        "tags": [
          "Status"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Your project ID from OAuth"
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    },
    "/papi/projects": {
      "get": {
        "operationId": "listMyProjects",
        "summary": "List all my projects",
        "description": "Returns all projects the authenticated user has access to. Use this to let users switch between projects without re-authentication.",
        "tags": [
          "Status"
        ],
        "responses": {
          "200": {
            "description": "List of projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "projects": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "project_id": {
                                "type": "integer"
                              },
                              "project_name": {
                                "type": "string"
                              },
                              "domain": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth": [
              "read"
            ]
          }
        ]
      },
      "post": {
        "operationId": "createProject",
        "summary": "Create a new project",
        "description": "Create a new website project. Each project gets its own subdomain, pages, assets, entities, and vault. Respects plan limits (Free: 1, Starter: 3, Pro: 10, Agency: unlimited). Returns the new project_id and live domain.",
        "tags": [
          "Status"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Project name (e.g., 'My Portfolio', 'Bakkerij De Gouden Korst')",
                    "example": "My Portfolio"
                  },
                  "subdomain": {
                    "type": "string",
                    "description": "Desired subdomain (lowercase, a-z0-9 and hyphens). Auto-generated from name if omitted. Result: {subdomain}.websitepublisher.ai",
                    "example": "my-portfolio"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Project created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string"
                        },
                        "subdomain": {
                          "type": "string"
                        },
                        "domain": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Plan limit reached — upgrade required"
          }
        },
        "security": [
          {
            "oauth": [
              "write"
            ]
          }
        ]
      }
    },
    "/papi/project/{projectId}/pages": {
      "get": {
        "summary": "List all pages",
        "operationId": "listPages",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      },
      "post": {
        "summary": "Create a page",
        "description": "Create HTML page. Use index.htm for homepage.",
        "operationId": "createPage",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Page"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/papi/project/{projectId}/pages/{pageSlug}": {
      "get": {
        "summary": "Get a page",
        "operationId": "getPage",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "about.htm"
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        },
        "description": "Get the full HTML content of a page. Response includes version and version_hash fields for use with patchPage."
      },
      "put": {
        "summary": "Update a page",
        "operationId": "updatePage",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PageUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          }
        },
        "description": "Replace the entire content of a page. For small changes, prefer patchPage which is more efficient and provides conflict detection."
      },
      "delete": {
        "summary": "Delete a page",
        "operationId": "deletePage",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          }
        }
      },
      "patch": {
        "summary": "Patch a page (surgical find/replace)",
        "description": "Apply targeted find/replace patches without sending full page content. Uses optimistic locking via base_version_hash to prevent conflicts. More efficient than updatePage for small edits.",
        "operationId": "patchPage",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "about.htm"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PagePatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Patches applied successfully. Returns new version number, version_hash, and count of patches applied."
          },
          "409": {
            "description": "Version conflict — page was modified since base_version_hash. Fetch latest content and retry."
          },
          "422": {
            "description": "Patch failed — find string not found or ambiguous (multiple matches)."
          }
        }
      }
    },
    "/papi/project/{projectId}/assets": {
      "get": {
        "summary": "List all assets",
        "description": "Returns all uploaded assets. Each asset includes a url field — always use this url in your HTML (e.g. img src, link href). Never construct URLs manually.",
        "operationId": "listAssets",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      },
      "post": {
        "summary": "Upload an asset",
        "description": "Upload a file (image, font, PDF, etc). Provide base64 content OR a source_url (public HTTPS URL) — not both. source_url is recommended for DALL-E/Unsplash images. Always use the returned url field in your HTML.",
        "operationId": "createAsset",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssetCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Uploaded"
          }
        }
      }
    },
    "/papi/project/{projectId}/assets/{assetId}": {
      "delete": {
        "summary": "Delete an asset",
        "description": "Permanently deletes an asset. Use the asset_id from list/upload responses.",
        "operationId": "deleteAsset",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Asset identifier from upload/list response",
            "example": "images/logo.png"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          }
        }
      }
    },
    "/mapi/project/{projectId}/entities": {
      "get": {
        "summary": "List all entities",
        "operationId": "listEntities",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      },
      "post": {
        "summary": "Create an entity",
        "operationId": "createEntity",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Entity"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/mapi/project/{projectId}/entities/{entityName}": {
      "put": {
        "summary": "Update entity metadata",
        "description": "Update entity metadata such as plural name, description, or public_read access. Set public_read to true to make entity data accessible without authentication via /mapi/public/{projectId}/{entity}.",
        "operationId": "updateEntity",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "blogpost"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EntityUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      },
      "delete": {
        "summary": "Delete an entity",
        "operationId": "deleteEntity",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          }
        }
      }
    },
    "/mapi/project/{projectId}/entities/{entityName}/schema": {
      "get": {
        "summary": "Get entity schema",
        "operationId": "getEntitySchema",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    },
    "/mapi/project/{projectId}/{entityType}": {
      "get": {
        "summary": "List records",
        "operationId": "listRecords",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "blogpost"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      },
      "post": {
        "summary": "Create record",
        "description": "Create new record. IMPORTANT: Fields are dynamic per entity. Call getEntitySchema FIRST to discover which fields exist. Only send fields from the schema. Never assume default fields.",
        "operationId": "createRecord",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenericRecord"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/mapi/project/{projectId}/{entityType}/{recordId}": {
      "get": {
        "summary": "Get record",
        "operationId": "getRecord",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      },
      "put": {
        "summary": "Update record",
        "description": "Update a record. Fields are dynamic per entity. Call getEntitySchema to discover available fields. Only send fields that exist in the schema.",
        "operationId": "updateRecord",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenericRecord"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      },
      "delete": {
        "summary": "Delete record",
        "operationId": "deleteRecord",
        "tags": [
          "Records"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entityType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          }
        }
      }
    },
    "/papi/project/{projectId}/pages/{pageSlug}/versions": {
      "get": {
        "summary": "Get page version history",
        "description": "Returns version metadata (version numbers, hashes, timestamps, change summaries) without page content. Use to inspect changes or find a version to rollback to.",
        "operationId": "getPageVersions",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "about.htm"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of version metadata objects, newest first."
          }
        }
      }
    },
    "/papi/project/{projectId}/pages/{pageSlug}/rollback": {
      "post": {
        "summary": "Rollback page to previous version",
        "description": "Creates a new version with the content from a previous version (forward-only, audit trail preserved). Specify target_version (number) or target_version_hash. Use getPageVersions to find available versions.",
        "operationId": "rollbackPage",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "about.htm"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PageRollback"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rollback successful. Returns new version number and hash."
          },
          "404": {
            "description": "Target version not found."
          },
          "422": {
            "description": "Cannot rollback — page has no version history or target is current version."
          }
        }
      }
    },
    "/papi/project/{projectId}/tracking": {
      "get": {
        "operationId": "getTrackingScripts",
        "summary": "Get tracking scripts",
        "description": "Get the current tracking scripts configured for a project.",
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current tracking scripts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "integer"
                        },
                        "head_scripts": {
                          "type": "string",
                          "nullable": true
                        },
                        "body_scripts": {
                          "type": "string",
                          "nullable": true
                        },
                        "has_tracking": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth": [
              "read"
            ]
          }
        ]
      },
      "put": {
        "operationId": "setTrackingScripts",
        "summary": "Set tracking scripts",
        "description": "Set tracking scripts injected into every page. head_scripts in head, body_scripts before body close. Omit or null to clear.",
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "head_scripts": {
                    "type": "string",
                    "description": "Scripts in head (GTM, Analytics)"
                  },
                  "body_scripts": {
                    "type": "string",
                    "description": "Scripts before body close (pixels, chat)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scripts updated"
          }
        },
        "security": [
          {
            "oauth": [
              "write"
            ]
          }
        ]
      },
      "delete": {
        "operationId": "removeTrackingScripts",
        "summary": "Remove tracking scripts",
        "description": "Remove all tracking scripts from a project.",
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scripts removed"
          }
        },
        "security": [
          {
            "oauth": [
              "write"
            ]
          }
        ]
      }
    },
    "/wpe/project/{projectId}/sessions": {
      "post": {
        "summary": "Create visual edit session",
        "description": "Creates a browser-based edit session for image management on a page. Returns an edit URL for the user. Use data-wpe-slot placeholder images when building pages, then create a session for real image uploads.",
        "operationId": "createEditSession",
        "tags": [
          "Visual Editor"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditSessionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Edit session created — returns session_id, edit_url, expires_in"
          },
          "404": {
            "description": "Page not found — create the page first"
          },
          "429": {
            "description": "Monthly or concurrent session limit reached"
          }
        }
      }
    },
    "/wpe/sessions/{sessionId}/changes": {
      "get": {
        "summary": "Get edit session changes",
        "description": "Reads back user changes from a visual edit session. Returns uploaded images with dimensions, style changes, and summary counts. Use after editing to adjust page layout.",
        "operationId": "getEditSessionChanges",
        "tags": [
          "Visual Editor"
        ],
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wpe_abc123..."
          }
        ],
        "responses": {
          "200": {
            "description": "Changelog with uploaded images, dimensions, style changes, and summary counts"
          },
          "404": {
            "description": "Session not found or expired"
          }
        }
      }
    },
    "/iapi/integrations": {
      "get": {
        "summary": "List available integrations",
        "description": "Public catalog of all integrations with endpoints and required secrets. No authentication required.",
        "operationId": "listAvailableIntegrations",
        "tags": [
          "Integrations"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Integration catalog"
          }
        }
      }
    },
    "/iapi/project/{projectId}/integrations": {
      "get": {
        "summary": "List configured integrations",
        "description": "Show which integrations are configured (ready) and which need setup for this project.",
        "operationId": "listConfiguredIntegrations",
        "tags": [
          "Integrations"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Configured and available integrations"
          }
        }
      }
    },
    "/iapi/project/{projectId}/integrations/{service}/setup": {
      "post": {
        "summary": "Setup integration",
        "description": "Configure an integration by storing its API keys in the vault. Use listAvailableIntegrations to see required secrets.",
        "operationId": "setupIntegration",
        "tags": [
          "Integrations"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "resend"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationSetup"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Integration configured"
          },
          "429": {
            "description": "Integration limit reached (upgrade plan)"
          }
        }
      }
    },
    "/iapi/project/{projectId}/integrations/{service}": {
      "delete": {
        "summary": "Remove integration",
        "description": "Remove an integration and permanently delete all its vault secrets.",
        "operationId": "removeIntegration",
        "tags": [
          "Integrations"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "resend"
          }
        ],
        "responses": {
          "200": {
            "description": "Integration removed, secrets deleted"
          }
        }
      }
    },
    "/iapi/project/{projectId}/{service}/{endpoint}": {
      "post": {
        "summary": "Execute integration",
        "description": "Execute an action (e.g. send email, create payment). Credentials resolved from vault. Must be configured first.",
        "operationId": "executeIntegration",
        "tags": [
          "Integrations"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "resend"
          },
          {
            "name": "endpoint",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "send-email"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationExecute"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Integration response"
          },
          "400": {
            "description": "Not configured or invalid input"
          },
          "429": {
            "description": "Proxy call limit reached (upgrade plan)"
          }
        }
      }
    },
    "/vapi/project/{projectId}/secrets": {
      "get": {
        "summary": "List vault secrets",
        "description": "List all secrets in the project vault. Returns metadata only (key names, status) — never actual values.",
        "operationId": "listVaultSecrets",
        "tags": [
          "Vault"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "service_type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by service (e.g. resend, mollie)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of secret metadata (never values)"
          }
        }
      },
      "post": {
        "summary": "Store a vault secret",
        "description": "Store or replace a secret. Encrypted with AES-256-GCM, can never be read back.",
        "operationId": "storeVaultSecret",
        "tags": [
          "Vault"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VaultSecretStore"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Secret stored"
          },
          "429": {
            "description": "Secret limit reached (upgrade plan)"
          }
        }
      }
    },
    "/vapi/project/{projectId}/secrets/{name}": {
      "delete": {
        "summary": "Delete a vault secret",
        "description": "Permanently delete a secret. The encrypted value is destroyed. Cannot be undone.",
        "operationId": "deleteVaultSecret",
        "tags": [
          "Vault"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Secret key name",
            "example": "resend_api_key"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "404": {
            "description": "Secret not found"
          }
        }
      }
    },
    "/sapi/project/{projectId}/forms": {
      "get": {
        "summary": "List configured forms",
        "description": "List all forms for a project with their required fields, actions, and submit limits.",
        "operationId": "listForms",
        "tags": [
          "Forms"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of form configurations"
          }
        }
      }
    },
    "/sapi/project/{projectId}/forms/configure": {
      "post": {
        "summary": "Configure a form",
        "description": "Create or update a form definition with required fields, submit action, and limits. Use listConfiguredIntegrations first to find services. IMPORTANT: use Safari ITP-proof pattern: store session_id in sessionStorage and send it as X-Session-Id header on the session GET and form submit calls.",
        "operationId": "configureForm",
        "tags": [
          "Forms"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FormConfig"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Form created"
          },
          "200": {
            "description": "Form updated"
          },
          "422": {
            "description": "Validation error"
          }
        }
      }
    },
    "/sapi/project/{projectId}/forms/{formName}": {
      "get": {
        "summary": "Get form config",
        "description": "Get a single form configuration with its fields, action, and limits.",
        "operationId": "getFormConfig",
        "tags": [
          "Forms"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "formName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "contact"
          }
        ],
        "responses": {
          "200": {
            "description": "Form configuration"
          },
          "404": {
            "description": "Form not found"
          }
        }
      },
      "delete": {
        "summary": "Delete form config",
        "description": "Remove a form configuration. Visitors can no longer submit this form.",
        "operationId": "deleteFormConfig",
        "tags": [
          "Forms"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "formName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "contact"
          }
        ],
        "responses": {
          "200": {
            "description": "Form deleted"
          },
          "404": {
            "description": "Form not found"
          }
        }
      }
    },
    "/papi/project/{projectId}/fragments": {
      "get": {
        "summary": "List fragments",
        "description": "List all reusable HTML fragments in a project. Returns fragment_name, include_tag, content and version info.",
        "operationId": "listFragments",
        "tags": [
          "Fragments"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of fragments with include_tag and content"
          }
        }
      },
      "post": {
        "summary": "Create fragment",
        "description": "Create a reusable HTML fragment. Returns include_tag to embed in pages: <!--#wps-include fragment=\"name\" -->. Use for shared elements like nav, footer.",
        "operationId": "createFragment",
        "tags": [
          "Fragments"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FragmentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Fragment created, includes include_tag"
          },
          "422": {
            "description": "Validation error"
          }
        }
      }
    },
    "/papi/project/{projectId}/fragments/{name}": {
      "put": {
        "summary": "Update fragment",
        "description": "Update a fragment HTML. All pages using its include_tag serve new content on next request. Cache invalidated automatically.",
        "operationId": "updateFragment",
        "tags": [
          "Fragments"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "menu"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FragmentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fragment updated, cache_invalidated: true"
          }
        }
      },
      "delete": {
        "summary": "Delete fragment",
        "description": "Delete a fragment. Pages using its include_tag render empty in its place.",
        "operationId": "deleteFragment",
        "tags": [
          "Fragments"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "menu"
          }
        ],
        "responses": {
          "200": {
            "description": "Fragment deleted"
          },
          "404": {
            "description": "Fragment not found"
          }
        }
      }
    },
    "/mapi/public/{projectId}/{entity}": {
      "get": {
        "summary": "List records (public)",
        "description": "List records of a public entity without authentication. Only available for entities with public_read enabled. Returns 404 for private or non-existing entities.",
        "operationId": "publicListRecords",
        "tags": [
          "Public"
        ],
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entity",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "showcase"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of records with pagination"
          },
          "404": {
            "description": "Entity not found or not public"
          }
        }
      }
    },
    "/mapi/public/{projectId}/{entity}/{recordId}": {
      "get": {
        "summary": "Get record (public)",
        "description": "Get a single record by ID from a public entity without authentication.",
        "operationId": "publicGetRecord",
        "tags": [
          "Public"
        ],
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "entity",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recordId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Record data"
          },
          "404": {
            "description": "Entity not public or record not found"
          }
        }
      }
    },
    "/iapi/project/{projectId}/integrations/{service}/schema": {
      "get": {
        "summary": "Get integration schema",
        "description": "Get all available endpoints, required fields, and input parameters for a specific integration. Call before executeIntegration to know exact endpoint names and input fields.",
        "operationId": "getIntegrationSchema",
        "tags": [
          "Integrations"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "resend"
          }
        ],
        "responses": {
          "200": {
            "description": "Integration schema with endpoints and required fields"
          },
          "404": {
            "description": "Integration not found"
          }
        }
      }
    },
    "/sapi/project/{projectId}/auth/config": {
      "get": {
        "summary": "Get visitor auth config",
        "description": "Get the current visitor authentication configuration for a project. Shows enabled status, allowed methods, and success redirect URL.",
        "operationId": "getVisitorAuthConfig",
        "tags": [
          "Visitor Auth"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Visitor auth configuration"
          }
        }
      },
      "post": {
        "summary": "Configure visitor auth",
        "description": "Enable email-based login for website visitors. Use method 'link' for magic link email or 'code' for 6-digit verification code. Visitors authenticate via the SAPI session flow.",
        "operationId": "configureVisitorAuth",
        "tags": [
          "Visitor Auth"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VisitorAuthConfig"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Visitor auth configured"
          }
        }
      }
    },
    "/aapi/project/{projectId}/tasks": {
      "get": {
        "summary": "List scheduled tasks",
        "description": "List all scheduled tasks for a project with their status, next run time, and last execution result.",
        "operationId": "listScheduledTasks",
        "tags": [
          "Scheduling"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "active_only",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of scheduled tasks"
          }
        }
      },
      "post": {
        "summary": "Create scheduled task",
        "description": "Create a task that runs automatically at a cron schedule. Use run_once=true for one-time actions (e.g. publish a page at a specific date/time). Supports publish_page, unpublish_page, and webhook actions.",
        "operationId": "createScheduledTask",
        "tags": [
          "Scheduling"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduledTaskCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Scheduled task created"
          },
          "429": {
            "description": "Task limit reached (upgrade plan)"
          }
        }
      }
    },
    "/aapi/project/{projectId}/tasks/{taskId}": {
      "delete": {
        "summary": "Delete scheduled task",
        "description": "Delete a scheduled task and all its run history.",
        "operationId": "deleteScheduledTask",
        "tags": [
          "Scheduling"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task deleted"
          }
        }
      }
    }
  }
}