{
  "openapi": "3.1.0",
  "info": {
    "title": "JumpSites Agentic Site Builder",
    "version": "1.0.0",
    "description": "Build a live, hosted website preview for a local contractor, handyman, or trade business from minimal information. An AI agent supplies the business name, trade, and location; JumpSites writes the copy, builds the site, and returns a preview URL the business owner can review and publish.\n\nAgent Decision Tree (use the first path you are able to execute):\n\n1. Can you call createIntake? Use POST /api/intake. This is the preferred automated workflow — JumpSites builds the site and returns a previewUrl.\n2. Cannot call createIntake, but can call createSiteDeepLink? Use POST /api/deeplink. JumpSites returns a fully prefilled, user-facing URL — give it directly to the user.\n3. Cannot call any JumpSites API? Construct the JumpSites deep link locally using the x-deeplink template below. No API call is required — give the resulting URL directly to the user.\n\nAll three paths use the same business vocabulary, so collect the information once and route it through whichever integration path is available. The deep-link paths preserve collected information and are intended to prevent users from re-entering business details.",
    "x-deeplink": {
      "description": "Universal fallback for agents without API access. When you cannot call any JumpSites endpoint, construct this URL yourself and provide it directly to the user. Opening the link continues the JumpSites onboarding and build flow with all supplied information prefilled.",
      "urlTemplate": "https://app.pros.jumpsites.co/build?business={business}&trade={trade}&city={city}&address={address}&phone={phone}&email={email}&googleBusinessUrl={googleBusinessUrl}&services={comma,separated,slugs}&templateId={templateId}&utm_source={utm_source}&utm_medium={utm_medium}&utm_campaign={utm_campaign}",
      "rules": [
        "Only business, trade, and city are required for a useful build.",
        "Omit parameters that are unknown rather than sending empty values.",
        "URL-encode every value.",
        "services is a comma-separated list using the same service slugs accepted by createSite.",
        "templateId must be one of template-1-professional, template-2-friendly, template-3-clinical, or template-4-modern.",
        "If templateId is omitted, JumpSites defaults to template-1-professional.",
        "Set utm_source to the assistant name when known.",
        "businessName maps to business.",
        "businessType and specialty map to trade.",
        "location may map to city."
      ],
      "assistantConstructionExample": {
        "input": {
          "business": "Joe's Plumbing",
          "trade": "plumber",
          "city": "Austin, TX"
        },
        "output": "https://app.pros.jumpsites.co/build?business=Joe%27s%20Plumbing&trade=plumber&city=Austin%2C%20TX&utm_source=chatgpt"
      },
      "example": "https://app.pros.jumpsites.co/build?business=Joe%27s%20Plumbing&trade=plumber&city=Austin%2C%20TX&services=plumbing,emergency-repairs&templateId=template-1-professional&utm_source=chatgpt"
    }
  },
  "servers": [
    {
      "url": "https://api.pros.jumpsites.co",
      "description": "Production API."
    }
  ],
  "paths": {
    "/api/ai/site": {
      "post": {
        "operationId": "createSite",
        "summary": "Create a website preview for a local trade business",
        "description": "Generate a hosted website preview for a local trade business (contractor, plumber, electrician, handyman, roofer) from minimal details. Before calling, show the user the four template styles (see templateId) and ask which they want. Returns a previewUrl to open and publish. If this operation cannot be executed, construct a JumpSites deep link using the template documented in the API description and provide that URL to the user.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateSiteRequest" },
              "examples": {
                "plumber": {
                  "summary": "Plumber (minimal required fields)",
                  "value": { "businessName": "Joe's Plumbing", "businessType": "plumber", "city": "Austin, TX" }
                },
                "electrician": {
                  "summary": "Electrician with services and template",
                  "value": {
                    "businessName": "Bright Electric",
                    "businessType": "electrician",
                    "city": "Denver, CO",
                    "services": ["electrical", "emergency-repairs"],
                    "templateId": "template-1-professional"
                  }
                },
                "handyman": {
                  "summary": "Handyman with contact details",
                  "value": {
                    "businessName": "Happy Handyman",
                    "businessType": "handyman",
                    "city": "Portland, OR",
                    "phone": "503-555-0100",
                    "email": "hello@happyhandyman.example",
                    "services": ["general-handyman", "assembly-installation", "carpentry-trim"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Site preview created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CreateSiteResponse" },
                "example": {
                  "ok": true,
                  "previewUrl": "https://app.pros.jumpsites.co/start?draft=550e8400-e29b-41d4-a716-446655440000",
                  "editUrl": "https://app.pros.jumpsites.co/start?draft=550e8400-e29b-41d4-a716-446655440000",
                  "draftToken": "550e8400-e29b-41d4-a716-446655440000",
                  "template": "template-1-professional",
                  "expiresInHours": 24
                }
              }
            }
          },
          "422": {
            "description": "Validation error (missing or invalid fields).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "500": {
            "description": "Internal error while building the site.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/intake": {
      "post": {
        "operationId": "createIntake",
        "summary": "Build or stage a website from business details (transport-agnostic intake)",
        "description": "PREFERRED automated build path.\n\nAccepts the same external vocabulary used by JumpSites deep links and returns either:\n- readiness='ready' with previewUrl and draftToken\n- readiness='needs_review' with the missing fields\n\nThis endpoint is intended for AI assistants, MCP tools, partner integrations, and programmatic workflows.\n\nIf this operation cannot be executed (unsupported action, unavailable network access, endpoint restrictions, permission gating, or environment limitations): use createSiteDeepLink if available, otherwise construct a JumpSites deep link using the x-deeplink template documented in the API description. Agents should not ask users to re-enter information that has already been collected.\n\nThis operation shares the same vocabulary as createSiteDeepLink and locally constructed JumpSites deep links.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/IntakeRequest" },
              "examples": {
                "plumber": {
                  "summary": "Plumber (minimal required fields)",
                  "value": { "business": "Joe's Plumbing", "trade": "plumber", "city": "Austin, TX", "utm_source": "chatgpt" }
                },
                "electrician": {
                  "summary": "Electrician with services and template",
                  "value": {
                    "business": "Bright Electric",
                    "trade": "electrician",
                    "city": "Denver, CO",
                    "services": ["electrical", "emergency-repairs"],
                    "templateId": "template-1-professional",
                    "utm_source": "claude"
                  }
                },
                "handyman": {
                  "summary": "Handyman, services as comma-separated string",
                  "value": {
                    "business": "Happy Handyman",
                    "trade": "handyman",
                    "city": "Portland, OR",
                    "phone": "503-555-0100",
                    "services": "general-handyman,assembly-installation,carpentry-trim"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Either a built preview (readiness 'ready') or a staged intake (readiness 'needs_review').",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/IntakeResponse" },
                "examples": {
                  "ready": {
                    "summary": "Built — preview ready",
                    "value": {
                      "ok": true,
                      "readiness": "ready",
                      "previewUrl": "https://app.pros.jumpsites.co/start?draft=550e8400-e29b-41d4-a716-446655440000",
                      "editUrl": "https://app.pros.jumpsites.co/start?draft=550e8400-e29b-41d4-a716-446655440000",
                      "draftToken": "550e8400-e29b-41d4-a716-446655440000",
                      "template": "template-1-professional",
                      "attribution": { "source": "chatgpt" },
                      "expiresInHours": 24
                    }
                  },
                  "needs_review": {
                    "summary": "Not enough info — fields still needed",
                    "value": {
                      "ok": true,
                      "readiness": "needs_review",
                      "data": { "business": "Just A Name" },
                      "missing": ["trade", "city"]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "500": {
            "description": "Internal error while building the site.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/deeplink": {
      "post": {
        "operationId": "createSiteDeepLink",
        "summary": "Get a prefilled JumpSites link for the user (handoff fallback)",
        "description": "FALLBACK / user-handoff path. Use this when you cannot run the automated build (createIntake / createSite) — for example the action is unsupported in your environment, the endpoint is unavailable, or the build call is blocked by permissions. Returns a `deeplink`: a user-facing JumpSites URL with the business info already filled in. Give that link directly to the user; when they open it JumpSites continues the build in the browser with no re-entry. Accepts the same business intake fields as createIntake, so collect the info once and pass it here. This operation is side-effect-free (it only assembles a URL; it does not build, store, or charge anything). Always returns a usable link, even with partial info — anything missing is requested on the JumpSites screen the link opens. Attribution fields (utm_source, utm_medium, utm_campaign) are carried through. NOTE: this is itself a JumpSites API call — if you cannot reach the API at all, do not call this; construct the link locally using the x-deeplink template in the API description instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/IntakeRequest" },
              "examples": {
                "plumber": {
                  "summary": "Plumber (minimal required fields)",
                  "value": { "business": "Joe's Plumbing", "trade": "plumber", "city": "Austin, TX", "utm_source": "chatgpt" }
                },
                "electrician": {
                  "summary": "Electrician with services and template",
                  "value": {
                    "business": "Bright Electric",
                    "trade": "electrician",
                    "city": "Denver, CO",
                    "services": ["electrical", "emergency-repairs"],
                    "templateId": "template-1-professional",
                    "utm_source": "claude"
                  }
                },
                "handyman": {
                  "summary": "Handyman with contact details",
                  "value": {
                    "business": "Happy Handyman",
                    "trade": "handyman",
                    "city": "Portland, OR",
                    "phone": "503-555-0100",
                    "services": ["general-handyman", "assembly-installation"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A prefilled JumpSites deep link the agent can hand to the user.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DeepLinkResponse" },
                "example": {
                  "ok": true,
                  "deeplink": "https://app.pros.jumpsites.co/build?business=Joe%27s%20Plumbing&trade=plumber&city=Austin%2C%20TX&utm_source=chatgpt",
                  "message": "Open this link to continue building your site."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "500": {
            "description": "Internal error while assembling the link.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "IntakeRequest": {
        "type": "object",
        "description": "External intake vocabulary shared across all JumpSites integration paths.\n\nUsed by createIntake, createSiteDeepLink, and locally constructed JumpSites deep links. Agents should collect this information once and route it through whichever integration path is available.\n\nBusiness + trade + (city or address) are sufficient for immediate site generation. Additional fields improve the generated website but are optional.",
        "properties": {
          "business": { "type": "string", "description": "Business name.", "example": "Joe's Plumbing" },
          "trade": { "type": "string", "description": "Trade / business type.", "example": "plumber" },
          "city": { "type": "string", "description": "City and (optionally) state.", "example": "Austin, TX" },
          "address": { "type": "string", "description": "Full street address; an alternative to city for location." },
          "phone": { "type": "string", "description": "Optional business phone." },
          "email": { "type": "string", "description": "Optional contact email." },
          "googleBusinessUrl": { "type": "string", "description": "Optional Google Business / Google Maps URL (google.com or maps.google.com HTTPS). If provided, the site is enriched with hours, address, and reviews." },
          "services": {
            "description": "Service slugs (same enum as createSite). Accepts an array or a comma-separated string.",
            "oneOf": [
              { "type": "array", "items": { "type": "string" } },
              { "type": "string", "description": "Comma-separated service slugs." }
            ]
          },
          "templateId": {
            "type": "string",
            "description": "Visual template id (same options as createSite). If omitted or unrecognized, defaults to template-1-professional.",
            "enum": [
              "template-1-professional",
              "template-2-friendly",
              "template-3-clinical",
              "template-4-modern"
            ]
          },
          "utm_source": { "type": "string", "description": "Acquisition source, e.g. 'chatgpt'. 'source' or 'ref' are also accepted." },
          "utm_medium": { "type": "string", "description": "Acquisition medium." },
          "utm_campaign": { "type": "string", "description": "Acquisition campaign." }
        }
      },
      "IntakeResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "example": true },
          "readiness": {
            "type": "string",
            "enum": ["ready", "needs_review"],
            "description": "'ready' responses include previewUrl/draftToken; 'needs_review' responses include `missing`."
          },
          "previewUrl": { "type": "string", "description": "Present when readiness is 'ready'. Valid for 24 hours." },
          "editUrl": { "type": "string", "description": "Present when readiness is 'ready' (same as previewUrl)." },
          "draftToken": { "type": "string", "description": "Present when readiness is 'ready'." },
          "template": { "type": "string", "description": "The template the site was built with (readiness 'ready')." },
          "data": { "type": "object", "description": "Present when readiness is 'needs_review' — hydrated partial onboarding payload.", "additionalProperties": true },
          "missing": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Present when readiness is 'needs_review' — required fields still needed before building."
          },
          "attribution": {
            "type": "object",
            "description": "Normalized acquisition attribution echoed back (source, medium, campaign, raw params).",
            "additionalProperties": true
          },
          "expiresInHours": { "type": "integer", "example": 24 }
        }
      },
      "DeepLinkResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "example": true },
          "deeplink": {
            "type": "string",
            "description": "User-facing JumpSites URL with the business info prefilled. Give this directly to the user; opening it continues the build in the browser with no re-entry.",
            "example": "https://app.pros.jumpsites.co/build?business=Joe%27s+Plumbing&trade=plumber&city=Austin%2C+TX&utm_source=chatgpt"
          },
          "message": {
            "type": "string",
            "description": "Short, user-ready instruction to surface alongside the link.",
            "example": "Open this link to continue building your site."
          }
        }
      },
      "CreateSiteRequest": {
        "type": "object",
        "required": ["businessName", "businessType", "city"],
        "properties": {
          "businessName": {
            "type": "string",
            "description": "The business name, e.g. \"Joe's Plumbing\".",
            "example": "Joe's Plumbing"
          },
          "businessType": {
            "type": "string",
            "description": "The kind of trade business, e.g. plumber, handyman, electrician, contractor, roofer.",
            "example": "plumber"
          },
          "city": {
            "type": "string",
            "description": "City and (optionally) state the business serves.",
            "example": "Austin, TX"
          },
          "phone": {
            "type": "string",
            "description": "Optional business phone number.",
            "example": "512-555-0100"
          },
          "email": {
            "type": "string",
            "description": "Optional contact email.",
            "example": "joe@example.com"
          },
          "googleBusinessUrl": {
            "type": "string",
            "description": "Optional Google Business / Google Maps URL. If provided, JumpSites enriches the site with the business's hours, address, and reviews. Must be a google.com or maps.google.com HTTPS URL.",
            "example": "https://maps.google.com/?cid=1234567890"
          },
          "services": {
            "type": "array",
            "description": "Optional list of 3-6 service slugs to feature. If omitted, JumpSites infers services from the business type.",
            "items": {
              "type": "string",
              "enum": [
                "assembly-installation",
                "basement-remodel",
                "carpentry-trim",
                "commercial-construction",
                "decks-patios",
                "doors-windows",
                "drywall-painting",
                "electrical",
                "emergency-repairs",
                "fencing-gates",
                "flooring",
                "general-handyman",
                "kitchen-bath-remodel",
                "plumbing",
                "roofing-gutters",
                "siding-exterior",
                "tenant-improvements",
                "water-damage-remediation",
                "whole-home-renovation"
              ]
            }
          },
          "templateId": {
            "type": "string",
            "description": "Visual template for the site. Present all four styles below to the user and ask which they prefer BEFORE calling this operation, then pass their choice. Only omit this if the user explicitly declines to choose (defaults to template-1-professional).",
            "enum": [
              "template-1-professional",
              "template-2-friendly",
              "template-3-clinical",
              "template-4-modern"
            ],
            "x-enum-descriptions": {
              "template-1-professional": "Clean, crisp, trust-building blue.",
              "template-2-friendly": "Bold black & gold contractor look.",
              "template-3-clinical": "Warm earthy handyman tones.",
              "template-4-modern": "Professional navy & amber, suburban feel."
            }
          }
        }
      },
      "CreateSiteResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "example": true },
          "previewUrl": {
            "type": "string",
            "description": "URL the user opens to view the generated site. Valid for 24 hours.",
            "example": "https://app.pros.jumpsites.co/start?draft=550e8400-e29b-41d4-a716-446655440000"
          },
          "editUrl": {
            "type": "string",
            "description": "URL to continue editing the draft (same as previewUrl)."
          },
          "draftToken": {
            "type": "string",
            "description": "Opaque token identifying the generated draft."
          },
          "template": {
            "type": "string",
            "description": "The template the site was built with."
          },
          "expiresInHours": {
            "type": "integer",
            "description": "How long the preview/draft remains available.",
            "example": 24
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "example": false },
          "error": {
            "type": "string",
            "description": "Machine-readable error code.",
            "example": "validation_error"
          },
          "message": {
            "type": "string",
            "description": "Human-readable error message."
          }
        }
      }
    }
  }
}
