{"openapi":"3.1.0","info":{"title":"Launchpad API","description":"Launchpad is a landing page builder. This API lets you create, manage, and publish landing pages programmatically.","version":"1.0.0"},"servers":[{"url":"/api/v1","description":"API v1"}],"paths":{"/api/v1/pages":{"get":{"operationId":"list-pages","summary":"List pages","description":"List all landing pages for the authenticated user. Supports pagination and filtering by published status.","tags":["Pages"],"responses":{"200":{"description":"Paginated list of pages","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Page"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"page","in":"query","description":"Page number (default: 1)","required":false,"schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","description":"Items per page (default: 20, max: 100)","required":false,"schema":{"type":"integer","default":20}},{"name":"is_published","in":"query","description":"Filter by published status","required":false,"schema":{"type":"boolean"}},{"name":"page_type","in":"query","description":"Filter by page type: \"user\" (default) or \"site\"","required":false,"schema":{"type":"string","enum":["user","site"]}}],"security":[{"bearerAuth":[]}]},"post":{"operationId":"create-page","summary":"Create a page","description":"Create a new landing page or site page. For user pages, provide title + slug. For site pages (CMS-managed marketing pages), set page_type to \"site\" and provide route_path instead of slug (requires admin role).","tags":["Pages"],"responses":{"200":{"description":"The created page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Page creation data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string","description":"Page title"},"slug":{"type":"string","description":"URL-safe slug (3-80 chars, lowercase alphanumeric and hyphens). Required for user pages, auto-generated for site pages."},"description":{"type":"string","description":"Page meta description"},"page_type":{"type":"string","enum":["user","site"],"description":"Page type: \"user\" (default) for landing pages, \"site\" for CMS-managed marketing pages (admin only)"},"route_path":{"type":"string","description":"Route path for site pages (e.g. \"/about\", \"/pricing\"). Required when page_type is \"site\"."}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/pages/{id}":{"get":{"operationId":"get-page","summary":"Get a page","description":"Retrieve a single page by ID, including its blocks.","tags":["Pages"],"responses":{"200":{"description":"The page with blocks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]},"patch":{"operationId":"update-page","summary":"Update a page","description":"Update page properties such as title, slug, description, or theme configuration.","tags":["Pages"],"responses":{"200":{"description":"The updated page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Fields to update","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string"},"theme_config":{"type":"object"},"route_path":{"type":"string","description":"Route path for site pages (e.g. \"/about\")"}}}}}},"security":[{"bearerAuth":[]}]},"delete":{"operationId":"delete-page","summary":"Delete a page","description":"Permanently delete a page and all its blocks.","tags":["Pages"],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/pages/{id}/publish":{"post":{"operationId":"publish-page","summary":"Publish a page","description":"Publish a page to make it publicly accessible at its slug URL.","tags":["Publishing"],"responses":{"200":{"description":"The published page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]},"delete":{"operationId":"unpublish-page","summary":"Unpublish a page","description":"Unpublish a page to remove it from public access.","tags":["Publishing"],"responses":{"200":{"description":"The unpublished page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/pages/{id}/blocks":{"get":{"operationId":"list-blocks","summary":"List blocks for a page","description":"Retrieve all content blocks for a page, ordered by sort_order.","tags":["Blocks"],"responses":{"200":{"description":"List of blocks","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Block"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]},"post":{"operationId":"create-block","summary":"Add a block to a page","description":"Create a new content block on a page. The block type determines the content schema.","tags":["Blocks"],"responses":{"200":{"description":"The created block","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Block"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Block creation data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["type"],"properties":{"type":{"type":"string","description":"Block type (hero, about, services, etc.)"},"content":{"type":"object","description":"Block content matching the type schema"},"sort_order":{"type":"integer","description":"Display order (default: appended to end)"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/pages/{id}/blocks/{blockId}":{"patch":{"operationId":"update-block","summary":"Update a block","description":"Update the content or properties of a block.","tags":["Blocks"],"responses":{"200":{"description":"The updated block","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Block"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"blockId","in":"path","description":"Block UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Fields to update","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"object","description":"Updated block content"},"sort_order":{"type":"integer"},"is_visible":{"type":"boolean"}}}}}},"security":[{"bearerAuth":[]}]},"delete":{"operationId":"delete-block","summary":"Delete a block","description":"Remove a content block from a page.","tags":["Blocks"],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"blockId","in":"path","description":"Block UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/pages/{id}/blocks/reorder":{"put":{"operationId":"reorder-blocks","summary":"Reorder blocks on a page","description":"Set the display order of blocks by providing an ordered array of block IDs.","tags":["Blocks"],"responses":{"200":{"description":"The reordered blocks","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Block"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Ordered block IDs","required":true,"content":{"application/json":{"schema":{"type":"object","required":["block_ids"],"properties":{"block_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Block UUIDs in desired display order"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/blog":{"get":{"operationId":"list-blog-posts","summary":"List blog posts","description":"Retrieve all blog posts, ordered by creation date (newest first).","tags":["Blog"],"responses":{"200":{"description":"List of blog posts","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BlogPost"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"create-blog-post","summary":"Create a blog post","description":"Create a new blog post. The post is created as a draft (unpublished). Use the publish endpoint to make it live.","tags":["Blog"],"responses":{"200":{"description":"The created blog post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Blog post creation data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","slug"],"properties":{"title":{"type":"string","description":"Post title"},"slug":{"type":"string","description":"URL-safe slug (3-80 chars, lowercase alphanumeric and hyphens)"},"excerpt":{"type":"string","description":"Short excerpt or summary"},"cover_image_url":{"type":"string","description":"URL for the cover image"},"body":{"type":"object","description":"Post body content (TipTap JSON format)"},"category":{"type":"string","description":"Post category"},"tags":{"type":"array","items":{"type":"string"},"description":"Post tags"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/blog/{postId}":{"get":{"operationId":"get-blog-post","summary":"Get a blog post","description":"Retrieve a single blog post by ID.","tags":["Blog"],"responses":{"200":{"description":"The blog post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"postId","in":"path","description":"Blog post UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]},"patch":{"operationId":"update-blog-post","summary":"Update a blog post","description":"Update blog post properties such as title, slug, body, excerpt, or tags.","tags":["Blog"],"responses":{"200":{"description":"The updated blog post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"postId","in":"path","description":"Blog post UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Fields to update","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"slug":{"type":"string"},"excerpt":{"type":"string"},"cover_image_url":{"type":"string"},"body":{"type":"object","description":"Post body content (TipTap JSON format)"},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}}}}},"security":[{"bearerAuth":[]}]},"delete":{"operationId":"delete-blog-post","summary":"Delete a blog post","description":"Permanently delete a blog post.","tags":["Blog"],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"postId","in":"path","description":"Blog post UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/blog/{postId}/publish":{"post":{"operationId":"publish-blog-post","summary":"Publish a blog post","description":"Publish a blog post to make it publicly visible. Sets published_at on first publish.","tags":["Blog"],"responses":{"200":{"description":"The published blog post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"postId","in":"path","description":"Blog post UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]},"delete":{"operationId":"unpublish-blog-post","summary":"Unpublish a blog post","description":"Unpublish a blog post to remove it from public visibility.","tags":["Blog"],"responses":{"200":{"description":"The unpublished blog post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"postId","in":"path","description":"Blog post UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/cal/event-types":{"get":{"operationId":"list-cal-event-types","summary":"List calendar event types","description":"List all Cal.com event types for the authenticated user.","tags":["Calendar"],"responses":{"200":{"description":"List of event types","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CalEventType"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"create-cal-event-type","summary":"Create a calendar event type","description":"Create a new Cal.com event type for the authenticated user.","tags":["Calendar"],"responses":{"200":{"description":"The created event type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalEventType"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Event type data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","slug","lengthInMinutes"],"properties":{"title":{"type":"string","description":"Event type title"},"slug":{"type":"string","description":"URL-safe slug"},"lengthInMinutes":{"type":"integer","description":"Duration in minutes"},"description":{"type":"string","description":"Event type description"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/cal/slots":{"get":{"operationId":"get-cal-slots","summary":"Get available time slots","description":"Get available booking time slots for a specific event type within a date range.","tags":["Calendar"],"responses":{"200":{"description":"Available slots grouped by date","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/CalSlot"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"eventTypeId","in":"query","description":"Event type ID","required":true,"schema":{"type":"integer"}},{"name":"startTime","in":"query","description":"Start of range (ISO 8601)","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"endTime","in":"query","description":"End of range (ISO 8601)","required":true,"schema":{"type":"string","format":"date-time"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/cal/bookings":{"get":{"operationId":"list-cal-bookings","summary":"List calendar bookings","description":"List all Cal.com bookings for the authenticated user.","tags":["Calendar"],"responses":{"200":{"description":"List of bookings","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CalBooking"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"create-cal-booking","summary":"Create a calendar booking","description":"Create a new booking for an event type. The booking is created on behalf of a visitor/attendee.","tags":["Calendar"],"responses":{"200":{"description":"The created booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalBooking"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Booking data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventTypeId","startTime","attendee"],"properties":{"eventTypeId":{"type":"integer","description":"Event type ID to book"},"startTime":{"type":"string","format":"date-time","description":"Booking start time (ISO 8601)"},"attendee":{"type":"object","required":["name","email","timeZone"],"properties":{"name":{"type":"string","description":"Attendee name"},"email":{"type":"string","description":"Attendee email"},"timeZone":{"type":"string","description":"Attendee timezone (e.g. America/New_York)"}}}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/cal/account":{"get":{"operationId":"get-cal-account","summary":"Get calendar account info","description":"Get the Cal.com account details for the authenticated user.","tags":["Calendar"],"responses":{"200":{"description":"Calendar account info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalAccount"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"security":[{"bearerAuth":[]}]}},"/api/v1/block-types":{"get":{"operationId":"list-block-types","summary":"List available block types","description":"Returns all available block types with their content schemas, defaults, and variant options.","tags":["Block Types"],"responses":{"200":{"description":"List of block type definitions","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"maxPerPage":{"type":"integer"},"variants":{"type":"array","items":{"type":"object"}}}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}}}},"/api/v1/templates":{"get":{"operationId":"list-templates","summary":"List page templates","description":"Returns all available page templates that can be used to create new pages.","tags":["Templates"],"responses":{"200":{"description":"List of templates","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"security":[{"bearerAuth":[]}]}},"/api/v1/pages/{id}/submissions":{"get":{"operationId":"list-submissions","summary":"List contact submissions","description":"Retrieve contact form submissions for a page.","tags":["Submissions"],"responses":{"200":{"description":"Paginated submissions","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Submission"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number","required":false,"schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","description":"Items per page","required":false,"schema":{"type":"integer","default":20}}],"security":[{"bearerAuth":[]}]}},"/api/v1/submissions":{"get":{"operationId":"list-all-submissions","summary":"List all submissions across pages","description":"Retrieve all contact form submissions across all pages owned by the authenticated user. Supports pagination.","tags":["Submissions"],"responses":{"200":{"description":"Paginated submissions across all pages","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Submission"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"page","in":"query","description":"Page number (default: 1)","required":false,"schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","description":"Items per page (default: 20, max: 100)","required":false,"schema":{"type":"integer","default":20}}],"security":[{"bearerAuth":[]}]}},"/api/v1/submissions/{id}":{"get":{"operationId":"get-submission","summary":"Get a submission","description":"Retrieve a single contact form submission by ID.","tags":["Submissions"],"responses":{"200":{"description":"The submission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Submission"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Submission UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]},"delete":{"operationId":"delete-submission","summary":"Delete a submission","description":"Permanently delete a contact form submission.","tags":["Submissions"],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Submission UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/upload":{"post":{"operationId":"upload-file","summary":"Upload a file","description":"Upload an image or file to the page-assets storage bucket. Accepts multipart/form-data.","tags":["Upload"],"responses":{"200":{"description":"Upload result with public URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Public URL of the uploaded file"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"File to upload (multipart/form-data)","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The file to upload"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/api-keys":{"get":{"operationId":"list-api-keys","summary":"List API keys","description":"List all API keys for the authenticated user. Secrets are not included.","tags":["API Keys"],"responses":{"200":{"description":"List of API keys (without secret values)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"create-api-key","summary":"Create an API key","description":"Generate a new API key with specified permissions. The full key is only shown once.","tags":["API Keys"],"responses":{"200":{"description":"The created API key with the full secret (shown only once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"API key creation data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","permissions"],"properties":{"name":{"type":"string","description":"Descriptive name for the key"},"permissions":{"type":"array","items":{"type":"string"},"description":"Permission scopes"},"expires_at":{"type":"string","format":"date-time","description":"Optional expiration date"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/api-keys/{id}":{"delete":{"operationId":"delete-api-key","summary":"Delete an API key","description":"Revoke and delete an API key.","tags":["API Keys"],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"API key UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/webhooks":{"get":{"operationId":"list-webhooks","summary":"List webhooks","description":"List all webhook subscriptions for the authenticated user, including last delivery status.","tags":["Webhooks"],"responses":{"200":{"description":"List of webhook subscriptions","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"create-webhook","summary":"Create a webhook","description":"Create a new webhook subscription. A signing secret is automatically generated.","tags":["Webhooks"],"responses":{"200":{"description":"The created webhook with secret (shown only once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Webhook creation data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","description":"The URL to receive webhook POST requests"},"events":{"type":"array","items":{"type":"string","enum":["page.created","page.updated","page.published","page.unpublished","page.deleted","block.created","block.updated","block.deleted","contact.created"]},"description":"Events to subscribe to"},"description":{"type":"string","description":"Optional description"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/webhooks/{id}":{"get":{"operationId":"get-webhook","summary":"Get a webhook","description":"Retrieve a webhook subscription with recent delivery history.","tags":["Webhooks"],"responses":{"200":{"description":"Webhook with recent deliveries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Webhook UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]},"patch":{"operationId":"update-webhook","summary":"Update a webhook","description":"Update webhook URL, events, active status, or description.","tags":["Webhooks"],"responses":{"200":{"description":"The updated webhook","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Webhook UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Fields to update","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"is_active":{"type":"boolean"},"description":{"type":"string"}}}}}},"security":[{"bearerAuth":[]}]},"delete":{"operationId":"delete-webhook","summary":"Delete a webhook","description":"Delete a webhook subscription and all its delivery history.","tags":["Webhooks"],"responses":{"200":{"description":"Deletion confirmation","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"id","in":"path","description":"Webhook UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"security":[{"bearerAuth":[]}]}},"/api/v1/ai/generate":{"post":{"operationId":"generate-page","summary":"Generate a page with AI","description":"Use AI to generate a complete landing page from a text prompt. Costs 5 rate limit units.","tags":["AI"],"responses":{"200":{"description":"The generated page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Generation prompt","required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","description":"Describe the landing page you want to create"},"template":{"type":"string","description":"Optional template ID to base the generation on"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/ai/generate-block":{"post":{"operationId":"generate-block","summary":"Generate a block with AI","description":"Use AI to generate content for a specific block type. Provide a block type and optional prompt describing the desired content.","tags":["AI"],"responses":{"200":{"description":"The generated block","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Block"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Block generation parameters","required":true,"content":{"application/json":{"schema":{"type":"object","required":["page_id","block_type"],"properties":{"page_id":{"type":"string","format":"uuid","description":"Page to generate the block for"},"block_type":{"type":"string","description":"Block type (hero, about, services, etc.)"},"prompt":{"type":"string","description":"Optional instructions for the AI"},"variant":{"type":"string","description":"Optional variant of the block type"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/ai/refine":{"post":{"operationId":"refine-block","summary":"Refine block content with AI","description":"Use AI to refine or improve the content of an existing block based on instructions.","tags":["AI"],"responses":{"200":{"description":"The refined block content","content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"object"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Refinement parameters","required":true,"content":{"application/json":{"schema":{"type":"object","required":["block_id","instructions"],"properties":{"block_id":{"type":"string","format":"uuid","description":"Block to refine"},"instructions":{"type":"string","description":"Instructions for how to refine the content"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/ai/suggest-content":{"post":{"operationId":"suggest-content","summary":"Get AI content suggestions for a block","description":"Use AI to suggest content improvements for an existing block based on a prompt.","tags":["AI"],"responses":{"200":{"description":"Suggested content","content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"object"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"requestBody":{"description":"Suggestion parameters","required":true,"content":{"application/json":{"schema":{"type":"object","required":["block_id","prompt"],"properties":{"block_id":{"type":"string","format":"uuid","description":"Block to get suggestions for"},"prompt":{"type":"string","description":"What kind of content suggestions to generate"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/schema":{"get":{"operationId":"get-openapi-schema","summary":"OpenAPI schema","description":"Returns the OpenAPI 3.1 specification for this API.","tags":["Discovery"],"responses":{"200":{"description":"OpenAPI 3.1 JSON specification","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}}}},"/api/v1/llms.txt":{"get":{"operationId":"get-llms-txt","summary":"LLM-readable API documentation","description":"Returns a plain-text summary of the API optimized for LLM consumption.","tags":["Discovery"],"responses":{"200":{"description":"Plain text API documentation","content":{"application/json":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}}}},"/api/contact/{pageId}":{"post":{"operationId":"contact-submit","summary":"Submit a contact form","description":"Submit a contact form entry for a published page. No authentication required.","tags":["Contact"],"responses":{"200":{"description":"Success"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}},"parameters":[{"name":"pageId","in":"path","description":"Page UUID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"description":"Contact form data","required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"name":{"type":"string","description":"Sender name"},"email":{"type":"string","description":"Sender email address"},"phone":{"type":"string","description":"Sender phone number"},"message":{"type":"string","description":"Contact message"}}}}}}}},"/api/webhooks/stripe":{"post":{"operationId":"stripe-webhook-handler","summary":"Stripe webhook handler","description":"Receives incoming Stripe webhook events. Verified via Stripe signature, not user auth.","tags":["Webhooks"],"responses":{"200":{"description":"Success"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - Insufficient permissions"},"429":{"description":"Rate limit exceeded"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"API key with lp_live_ prefix. Example: Bearer lp_live_abc123..."},"sessionAuth":{"type":"apiKey","in":"cookie","name":"sb-access-token","description":"Supabase session cookie. Admin endpoints require role=admin."}},"schemas":{"Page":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"page_type":{"type":"string","enum":["user","site"]},"route_path":{"type":"string","nullable":true},"is_published":{"type":"boolean"},"published_at":{"type":"string","format":"date-time","nullable":true},"theme_config":{"type":"object"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"BlogPost":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid","nullable":true},"title":{"type":"string"},"slug":{"type":"string"},"excerpt":{"type":"string","nullable":true},"cover_image_url":{"type":"string","nullable":true},"body":{"type":"object","description":"Post body content in TipTap JSON format"},"category":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"is_published":{"type":"boolean"},"published_at":{"type":"string","format":"date-time","nullable":true},"reading_time":{"type":"integer","description":"Estimated reading time in minutes"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Block":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"page_id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["hero","about","services","testimonials","cta","footer","pricing","faq","contact","features","logo-cloud","metrics","newsletter","blog-preview","booking"]},"sort_order":{"type":"integer"},"content":{"type":"object"},"is_visible":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"is_active":{"type":"boolean"},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Submission":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"page_id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"message":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"key_prefix":{"type":"string"},"permissions":{"type":"array","items":{"type":"string"}},"last_used_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"Template":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"blocks":{"type":"array","items":{"$ref":"#/components/schemas/Block"}}}},"CalEventType":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"lengthInMinutes":{"type":"integer"},"hidden":{"type":"boolean"}}},"CalBooking":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"startTime":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"date-time"},"status":{"type":"string"},"attendees":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}}}}}},"CalSlot":{"type":"object","properties":{"time":{"type":"string","format":"date-time"}}},"CalAccount":{"type":"object","properties":{"cal_username":{"type":"string"},"cal_email":{"type":"string"},"default_event_type_id":{"type":"integer","nullable":true},"is_active":{"type":"boolean"}}},"PaginationMeta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}}}}},"security":[{"bearerAuth":[]}]}