{
  "openapi": "3.1.0",
  "info": {
    "title": "CarveLink REST API",
    "version": "1.0.0",
    "summary": "Programmatic access to CarveLink bio pages, blocks, themes, analytics, A/B testing, commerce, and account data.",
    "description": "The CarveLink REST API lets you build and manage bio pages, blocks, themes, and A/B experiments from any language. It powers the official CarveLink MCP server (npm: carvelink-mcp) and can be called directly. Rate limit: 60 requests per minute per key. Errors follow the shape { error: string, ...details }.",
    "contact": {
      "name": "CarveLink Support",
      "email": "support@carvelink.com",
      "url": "https://carvelink.com/docs/api"
    },
    "license": {
      "name": "Proprietary: see carvelink.com/terms",
      "url": "https://carvelink.com/terms"
    },
    "termsOfService": "https://carvelink.com/terms"
  },
  "servers": [
    {
      "url": "https://carvelink.com/api/v1",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Human-readable REST API documentation",
    "url": "https://carvelink.com/docs/api"
  },
  "security": [
    { "bearerAuth": [] }
  ],
  "tags": [
    { "name": "Pages", "description": "Create, read, update, and delete bio pages." },
    { "name": "Blocks", "description": "Content blocks that make up a page." },
    { "name": "Theme", "description": "Visual theming for a page." },
    { "name": "Analytics", "description": "Page views, clicks, and A/B test results." },
    { "name": "A/B Testing", "description": "Create variants and read experiment results." },
    { "name": "Account", "description": "Account metadata, usage counters, earnings." },
    { "name": "Commerce", "description": "Digital products the account has for sale." },
    { "name": "Utilities", "description": "Image uploads, block schema introspection, link metadata." }
  ],
  "paths": {
    "/pages": {
      "get": {
        "operationId": "listPages",
        "summary": "List pages for the authenticated account.",
        "tags": ["Pages"],
        "responses": {
          "200": {
            "description": "Array of pages.",
            "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Page" } } } }
          }
        }
      },
      "post": {
        "operationId": "createPage",
        "summary": "Create a new bio page.",
        "tags": ["Pages"],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PageCreate" } } }
        },
        "responses": {
          "201": { "description": "Page created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Page" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/pages/{id}": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "get": {
        "operationId": "getPage",
        "summary": "Get a single page by id.",
        "tags": ["Pages"],
        "responses": {
          "200": { "description": "Page.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Page" } } } },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "put": {
        "operationId": "updatePage",
        "summary": "Update page metadata (title, description, slug, published status, variant/traffic split settings).",
        "tags": ["Pages"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PageUpdate" } } } },
        "responses": { "200": { "description": "Updated page.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Page" } } } } }
      },
      "delete": {
        "operationId": "deletePage",
        "summary": "Delete a page.",
        "tags": ["Pages"],
        "responses": { "204": { "description": "Deleted." } }
      }
    },
    "/pages/{id}/preview-link": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "post": {
        "operationId": "createPreviewLink",
        "summary": "Generate a private, short-lived preview URL for a draft page.",
        "tags": ["Pages"],
        "responses": { "201": { "description": "Preview link.", "content": { "application/json": { "schema": { "type": "object", "properties": { "preview_url": { "type": "string", "format": "uri" }, "expires_at": { "type": "string", "format": "date-time" } } } } } } }
      }
    },
    "/pages/{id}/submissions": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "get": {
        "operationId": "getFormSubmissions",
        "summary": "List form-block submissions on a page.",
        "tags": ["Pages"],
        "responses": { "200": { "description": "Submissions." } }
      }
    },
    "/pages/{id}/blocks": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "get": {
        "operationId": "listBlocks",
        "summary": "List all blocks on a page in display order.",
        "tags": ["Blocks"],
        "responses": { "200": { "description": "Blocks.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Block" } } } } } }
      },
      "post": {
        "operationId": "addBlock",
        "summary": "Add a block to a page. Body must include `type` and `content` matching the block's schema.",
        "tags": ["Blocks"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlockCreate" } } } },
        "responses": { "201": { "description": "Block created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Block" } } } } }
      }
    },
    "/blocks/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
      "put": {
        "operationId": "updateBlock",
        "summary": "Update a block's content, visibility, or scheduling.",
        "tags": ["Blocks"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlockUpdate" } } } },
        "responses": { "200": { "description": "Updated block.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Block" } } } } }
      },
      "delete": {
        "operationId": "deleteBlock",
        "summary": "Delete a block.",
        "tags": ["Blocks"],
        "responses": { "204": { "description": "Deleted." } }
      }
    },
    "/pages/{id}/blocks/reorder": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "put": {
        "operationId": "reorderBlocks",
        "summary": "Set block display order for a page.",
        "tags": ["Blocks"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["block_ids"], "properties": { "block_ids": { "type": "array", "items": { "type": "string" } } } } } } },
        "responses": { "200": { "description": "Reordered." } }
      }
    },
    "/pages/{id}/theme": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "put": {
        "operationId": "updateTheme",
        "summary": "Update a page's theme (preset, colors, fonts, backgrounds, button styles).",
        "tags": ["Theme"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Theme" } } } },
        "responses": { "200": { "description": "Updated theme." } }
      }
    },
    "/pages/{id}/analytics": {
      "parameters": [
        { "$ref": "#/components/parameters/PageId" },
        { "name": "days", "in": "query", "schema": { "type": "integer", "default": 7, "minimum": 1, "maximum": 90 } }
      ],
      "get": {
        "operationId": "getAnalytics",
        "summary": "Page analytics: views, clicks, top links, referrers, device breakdown, daily rollups.",
        "tags": ["Analytics"],
        "responses": { "200": { "description": "Analytics snapshot.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Analytics" } } } } }
      }
    },
    "/pages/{id}/variants": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "post": {
        "operationId": "createVariant",
        "summary": "Create an A/B variant of a page. Duplicates the page and sets a traffic split.",
        "tags": ["A/B Testing"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "variant_label": { "type": "string" }, "traffic_split": { "type": "integer", "minimum": 1, "maximum": 99 } } } } } },
        "responses": { "201": { "description": "Variant page created." } }
      }
    },
    "/pages/{id}/experiment": {
      "parameters": [{ "$ref": "#/components/parameters/PageId" }],
      "get": {
        "operationId": "getExperimentResults",
        "summary": "Read A/B experiment results with two-proportion Z-test significance.",
        "tags": ["A/B Testing"],
        "responses": { "200": { "description": "Experiment results." } }
      }
    },
    "/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Account metadata: email, tier, connect status, subscription.",
        "tags": ["Account"],
        "responses": { "200": { "description": "Account." } }
      }
    },
    "/account/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Current tier limits and usage: pages, blocks, API calls, storage.",
        "tags": ["Account"],
        "responses": { "200": { "description": "Usage." } }
      }
    },
    "/earnings": {
      "get": {
        "operationId": "getEarnings",
        "summary": "Stripe Connect earnings summary: gross, net, fees, transaction count.",
        "tags": ["Account"],
        "responses": { "200": { "description": "Earnings." } }
      }
    },
    "/products": {
      "get": {
        "operationId": "listProducts",
        "summary": "List digital products this account has for sale.",
        "tags": ["Commerce"],
        "responses": { "200": { "description": "Products." } }
      }
    },
    "/upload/image": {
      "post": {
        "operationId": "uploadImage",
        "summary": "Upload an image via base64 or source URL. Returns a hosted URL.",
        "tags": ["Utilities"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "string", "description": "base64-encoded image bytes" }, "source_url": { "type": "string", "format": "uri", "description": "URL to fetch and rehost" }, "filename": { "type": "string" } } } } } },
        "responses": { "201": { "description": "Uploaded.", "content": { "application/json": { "schema": { "type": "object", "properties": { "url": { "type": "string", "format": "uri" } } } } } } }
      }
    },
    "/block-schemas": {
      "get": {
        "operationId": "listBlockSchemas",
        "summary": "List all supported block types and their JSON schemas.",
        "tags": ["Utilities"],
        "responses": { "200": { "description": "Block schemas." } }
      }
    },
    "/block-schemas/{type}": {
      "parameters": [{ "name": "type", "in": "path", "required": true, "schema": { "type": "string" } }],
      "get": {
        "operationId": "getBlockSchema",
        "summary": "Fetch the JSON schema for a single block type.",
        "tags": ["Utilities"],
        "responses": { "200": { "description": "Block schema." } }
      }
    },
    "/link-metadata": {
      "parameters": [{ "name": "url", "in": "query", "required": true, "schema": { "type": "string", "format": "uri" } }],
      "get": {
        "operationId": "fetchLinkMetadata",
        "summary": "Fetch Open Graph metadata for a URL (title, description, image). Used to build rich link previews.",
        "tags": ["Utilities"],
        "responses": { "200": { "description": "Metadata." } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Opaque token. Two shapes: cv_live_* (long-lived API key created in the admin) or cv_oauth_* (OAuth 2.1 + PKCE short-lived token, refreshable)."
      }
    },
    "parameters": {
      "PageId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
    },
    "responses": {
      "BadRequest": { "description": "Invalid input.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": { "type": "object", "properties": { "error": { "type": "string" }, "details": { "type": "object" } } },
      "Page": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "user_id": { "type": "string", "format": "uuid" },
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "description": { "type": ["string", "null"] },
          "published": { "type": "boolean" },
          "variant_of": { "type": ["string", "null"], "format": "uuid" },
          "variant_label": { "type": ["string", "null"] },
          "traffic_split": { "type": ["integer", "null"] },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "PageCreate": {
        "type": "object",
        "required": ["slug", "title"],
        "properties": {
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "description": { "type": "string" },
          "published": { "type": "boolean", "default": false }
        }
      },
      "PageUpdate": {
        "type": "object",
        "properties": {
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "description": { "type": "string" },
          "published": { "type": "boolean" },
          "variant_label": { "type": "string" },
          "traffic_split": { "type": "integer" }
        }
      },
      "Block": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "page_id": { "type": "string", "format": "uuid" },
          "type": { "type": "string", "description": "One of the 23 block types: link, header, social_grid, video_embed, image, text, spacer, form, email_capture, tip_jar, digital_product, faq, testimonial, countdown, gallery, map, whatsapp, shopify, wordpress, instagram, tiktok_feed, embed." },
          "content": { "type": "object" },
          "order": { "type": "integer" },
          "visible": { "type": "boolean" },
          "publish_at": { "type": ["string", "null"], "format": "date-time" },
          "expire_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "BlockCreate": {
        "type": "object",
        "required": ["type", "content"],
        "properties": {
          "type": { "type": "string" },
          "content": { "type": "object" },
          "order": { "type": "integer" }
        }
      },
      "BlockUpdate": {
        "type": "object",
        "properties": {
          "content": { "type": "object" },
          "visible": { "type": "boolean" },
          "publish_at": { "type": "string", "format": "date-time" },
          "expire_at": { "type": "string", "format": "date-time" }
        }
      },
      "Theme": {
        "type": "object",
        "properties": {
          "preset": { "type": "string" },
          "font_family": { "type": "string" },
          "colors": { "type": "object" },
          "background": { "type": "object", "description": "Type: solid | gradient | image. See docs for shape." },
          "button_style": { "type": "string" },
          "card_style": { "type": "string" }
        }
      },
      "Analytics": {
        "type": "object",
        "properties": {
          "summary": { "type": "object" },
          "daily": { "type": "array", "items": { "type": "object" } },
          "devices": { "type": "array", "items": { "type": "object" } },
          "top_referrers": { "type": "array", "items": { "type": "object" } },
          "top_links": { "type": "array", "items": { "type": "object" } }
        }
      }
    }
  }
}
