{
  "openapi": "3.1.0",
  "info": {
    "title": "Varde Labs Site API",
    "summary": "Lead-capture endpoints on vardelabs.com",
    "description": "Machine-readable specification of the callable HTTP surfaces on www.vardelabs.com.\n\nBoth endpoints are public form-submission APIs backed by Cloudflare Functions. They create leads and send acknowledgment or deliverable email; they are not general-purpose data APIs.\n\nAgent guidance:\n- Only submit on behalf of a user who explicitly asked for it. Never fabricate contact details.\n- No authentication is required. There is no per-agent quota today; pace requests politely per /AGENTS.md.\n- Responses echo the request Origin in Access-Control-Allow-Origin; preflight OPTIONS is accepted.\n- Usage terms live in /AGENTS.md and /llms-full.txt: grounded use permitted, training prohibited.",
    "version": "1.0.0",
    "contact": {
      "name": "Varde Labs",
      "email": "hello@vardelabs.com",
      "url": "https://www.vardelabs.com/for-ai/"
    }
  },
  "servers": [
    {
      "url": "https://www.vardelabs.com",
      "description": "Production site (the apex vardelabs.com 301s here)"
    }
  ],
  "paths": {
    "/api/contact": {
      "post": {
        "operationId": "submitContact",
        "summary": "Create a HubSpot contact and optionally email a deliverable",
        "description": "Primary lead-capture endpoint. Creates a HubSpot contact (deduplicated by email) and, when a known deliverable key is supplied, asynchronously sends the matching acknowledgment or download email via Resend.\n\nUsed by the homepage Groundwork quote form, the finance case-study download form, and the Varde Creative intro form on design.vardelabs.com.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Contact created. The deliverable email, if any, is sent asynchronously.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ContactCreated" },
                "example": { "success": true, "id": "1029384756" }
              }
            }
          },
          "200": {
            "description": "A contact with this email already exists. Treated as success; the deliverable email still sends if requested.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Acknowledgment" },
                "example": { "success": true, "message": "Contact already exists." }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "500": {
            "description": "Server configuration error (for example a missing provider API key). Retry will not help until the operator fixes it.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": "Server configuration error: API key missing.", "code": "SERVER_MISCONFIGURED", "hint": "This is an operator-side fault, not a request problem. Retry after the site operator resolves it." }
              }
            }
          },
          "502": {
            "description": "The upstream CRM rejected the create. Safe to retry later.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": "Could not create contact. Please try again.", "code": "UPSTREAM_REJECTED", "hint": "The CRM rejected the create. Safe to retry the identical request later." }
              }
            }
          },
          "503": {
            "description": "Upstream CRM unreachable. Safe to retry later.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": "Service unavailable. Please try again.", "code": "UPSTREAM_UNAVAILABLE", "hint": "The CRM is unreachable. Safe to retry with backoff." }
              }
            }
          }
        }
      },
      "options": {
        "operationId": "contactPreflight",
        "summary": "CORS preflight",
        "responses": {
          "204": {
            "description": "Preflight accepted. Allows any origin with Content-Type headers."
          }
        }
      }
    },
    "/api/roadmap": {
      "post": {
        "operationId": "requestRoadmap",
        "summary": "Register interest in a tailored AI roadmap",
        "description": "Lightweight capture endpoint that logs a work-email address as a roadmap request. Free consumer mail domains are rejected; a business domain is expected.\n\nNo page currently calls this endpoint (the North Star form it served was replaced by a booking CTA), but it remains live and behaves as specified here.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RoadmapRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request received.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Acknowledgment" },
                "example": { "success": true, "message": "Roadmap request received. North is on it." }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" }
        }
      },
      "options": {
        "operationId": "roadmapPreflight",
        "summary": "CORS preflight",
        "responses": {
          "204": {
            "description": "Preflight accepted. Allows any origin with Content-Type headers."
          }
        }
      }
    }
  },
  "webhooks": {
    "stripeCheckoutCompleted": {
      "post": {
        "operationId": "stripeWebhook",
        "summary": "Stripe checkout fulfillment receiver (not agent-callable)",
        "description": "POST /api/stripe-webhook receives Stripe events (currently fulfilling the $19 AI Assistant Playbook). Requests must carry a valid Stripe-Signature header tied to the account webhook secret; unsigned calls return 400. Listed for completeness only: external callers cannot invoke this endpoint meaningfully.",
        "responses": {
          "200": { "description": "Event received (or deliberately ignored with receipt)." },
          "400": {
            "description": "Invalid signature or invalid JSON.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": "Invalid signature" }
              }
            }
          },
          "500": { "description": "Webhook secret not configured server-side." }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ContactRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["email"],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Contact email. Required; must match a basic email shape."
          },
          "firstname": {
            "type": "string",
            "description": "Optional first name, stored on the HubSpot contact."
          },
          "lastname": {
            "type": "string",
            "description": "Optional last name, stored on the HubSpot contact."
          },
          "company": {
            "type": "string",
            "description": "Optional company name, stored on the HubSpot contact."
          },
          "source": {
            "type": "string",
            "description": "Form identifier stored as form_source. Known values today: finance-case-study, groundwork-quote, creative-intro. Unknown values are stored verbatim without owner routing."
          },
          "lifecycleStage": {
            "type": "string",
            "description": "HubSpot lifecycle stage. Invalid or missing values fall back to lead.",
            "enum": [
              "subscriber",
              "lead",
              "marketingqualifiedlead",
              "salesqualifiedlead",
              "opportunity",
              "customer",
              "evangelist",
              "other"
            ],
            "default": "lead"
          },
          "deliverable": {
            "type": "string",
            "description": "Key selecting an automated follow-up email. Unknown keys are ignored silently.",
            "enum": ["finance-case-study", "groundwork-quote", "creative-intro"]
          },
          "message": {
            "type": "string",
            "maxLength": 4000,
            "description": "Free-text note stored in the HubSpot message property. Truncated server-side to 4000 characters."
          }
        }
      },
      "RoadmapRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["email"],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Work email. Free consumer providers (gmail, outlook, icloud, protonmail, and similar) are rejected with 422."
          }
        }
      },
      "Acknowledgment": {
        "type": "object",
        "required": ["success", "message"],
        "properties": {
          "success": { "type": "boolean", "const": true },
          "message": { "type": "string" }
        }
      },
      "ContactCreated": {
        "type": "object",
        "required": ["success", "id"],
        "properties": {
          "success": { "type": "boolean", "const": true },
          "id": {
            "type": "string",
            "description": "HubSpot contact id of the created record."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error", "code", "hint"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable summary. Do not branch on this string; use code."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error identifier. Branch on this.",
            "enum": [
              "INVALID_JSON",
              "INVALID_EMAIL",
              "FREE_PROVIDER_EMAIL",
              "SERVER_MISCONFIGURED",
              "UPSTREAM_REJECTED",
              "UPSTREAM_UNAVAILABLE",
              "INVALID_SIGNATURE",
              "METHOD_NOT_ALLOWED",
              "NOT_FOUND"
            ]
          },
          "hint": {
            "type": "string",
            "description": "Resolution guidance: what to change, or whether retrying helps."
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Body is not valid JSON.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Invalid JSON", "code": "INVALID_JSON", "hint": "Send a valid JSON request body with Content-Type: application/json." }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Email missing, malformed, or (on /api/roadmap) a free-provider domain.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "examples": {
              "invalidEmail": {
                "value": {
                  "error": "A valid email is required.",
                  "code": "INVALID_EMAIL",
                  "hint": "Provide an email property matching a standard email format."
                }
              },
              "freeProvider": {
                "value": {
                  "error": "Please use your work email to receive a tailored roadmap.",
                  "code": "FREE_PROVIDER_EMAIL",
                  "hint": "Free consumer mail domains are not accepted. Use a business email address."
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "The URL path is not a real endpoint under /api/.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "Not found. This endpoint does not exist.",
              "code": "NOT_FOUND",
              "hint": "See https://www.vardelabs.com/openapi.json for the list of valid endpoints."
            }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "The path exists but does not accept this method. All /api/ endpoints accept POST only (plus OPTIONS preflight).",
        "headers": {
          "Allow": {
            "description": "Methods accepted by this endpoint: POST, OPTIONS.",
            "schema": { "type": "string", "const": "POST, OPTIONS" }
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "Method not allowed on this endpoint.",
              "code": "METHOD_NOT_ALLOWED",
              "hint": "This endpoint accepts POST (and OPTIONS preflight). See https://www.vardelabs.com/openapi.json for its request schema."
            }
          }
        }
      }
    }
  }
}
