{
  "openapi": "3.1.0",
  "info": {
    "title": "Sean Guillermo — Site API",
    "description": "Public API for seanguillermo.com: project inquiries, newsletter signup, service listing, health, and an MCP endpoint. Discoverable via /.well-known/api-catalog (RFC 9727).\n\n**Errors:** every error is RFC 9457 `application/problem+json` with `type`, `title`, `status`, `detail`, `instance`, and `code`. Legacy `error` and `requestId` members are kept for older clients; quote `requestId` when reporting a failure.\n\n**Rate limits:** every response carries `RateLimit-Policy` / `RateLimit` (IETF httpapi draft) and `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset`. A 429 also sends `Retry-After`.\n\n**Versioning:** the current major version is 1, sent as the `API-Version` response header. `/api/v1/*` is a stable alias of `/api/*`; pin to it for long-lived integrations. Breaking changes ship only under a new major prefix (`/api/v2/*`).\n\n**Deprecation policy:** a deprecated endpoint keeps working for at least 6 months. During that window its responses carry a `Deprecation` header (RFC 9745), a `Sunset` header (RFC 8594) with the removal date, and a `Link` with `rel=\"deprecation\"` pointing to https://www.seanguillermo.com/developers#versioning. No endpoint is deprecated today.",
    "version": "1.2.0",
    "contact": {
      "name": "Sean Guillermo",
      "url": "https://www.seanguillermo.com/contact",
      "email": "strategy@seanguillermo.com"
    },
    "termsOfService": "https://www.seanguillermo.com/privacy"
  },
  "servers": [
    {
      "url": "https://www.seanguillermo.com",
      "description": "Production (unversioned paths, current major version)"
    },
    {
      "url": "https://www.seanguillermo.com/api/v1",
      "description": "Version-pinned alias — paths drop the /api prefix, e.g. POST /contact"
    }
  ],
  "tags": [
    {
      "name": "Leads"
    },
    {
      "name": "Services"
    },
    {
      "name": "Meta"
    },
    {
      "name": "Agents"
    }
  ],
  "components": {
    "securitySchemes": {
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "session",
        "description": "Session cookie set by POST /api/auth/login (member and admin accounts)."
      }
    },
    "headers": {
      "RateLimitPolicy": {
        "description": "Quota policy, e.g. \"default\";q=5;w=60",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit": {
        "description": "Remaining quota and seconds until reset, e.g. \"default\";r=4;t=60",
        "schema": {
          "type": "string"
        }
      },
      "RateLimitLimit": {
        "description": "Requests allowed per window",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Requests left in the current window",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitReset": {
        "description": "Seconds until the window resets",
        "schema": {
          "type": "integer"
        }
      },
      "RetryAfter": {
        "description": "Seconds until the client may retry",
        "schema": {
          "type": "integer"
        }
      },
      "ApiVersion": {
        "description": "Major API version that served the request",
        "schema": {
          "type": "string",
          "example": "1"
        }
      },
      "RequestId": {
        "description": "Correlation ID; same value as requestId in bodies",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details.",
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Problem type URI",
            "example": "https://www.seanguillermo.com/developers#errors-validation-failed"
          },
          "title": {
            "type": "string",
            "example": "Validation Failed"
          },
          "status": {
            "type": "integer",
            "example": 400
          },
          "detail": {
            "type": "string",
            "example": "Valid email required"
          },
          "instance": {
            "type": "string",
            "example": "/api/contact"
          },
          "code": {
            "type": "string",
            "enum": [
              "bad-request",
              "validation-failed",
              "unauthorized",
              "forbidden",
              "not-found",
              "method-not-allowed",
              "unsupported-media-type",
              "rate-limited",
              "server-error"
            ]
          },
          "errors": {
            "type": "array",
            "description": "Field-level validation errors",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "error": {
            "type": "string",
            "description": "Legacy alias of detail"
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "SuccessResponse": {
        "type": "object",
        "required": [
          "success",
          "requestId"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "status",
          "service",
          "timestamp"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          },
          "service": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PricingTier": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "service_id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "price_label": {
            "type": "string"
          },
          "price_note": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_popular": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "sort_order": {
            "type": "integer"
          }
        }
      },
      "Service": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "is_active": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "sort_order": {
            "type": "integer"
          },
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PricingTier"
            }
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "tools/list",
              "tools/call"
            ]
          },
          "params": {
            "type": "object"
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "result": {
            "type": "object"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Malformed body or failed validation",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "API-Version": {
            "$ref": "#/components/headers/ApiVersion"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid session",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "API-Version": {
            "$ref": "#/components/headers/ApiVersion"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but not allowed",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "API-Version": {
            "$ref": "#/components/headers/ApiVersion"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "Body is not JSON or an HTML form",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "API-Version": {
            "$ref": "#/components/headers/ApiVersion"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "API-Version": {
            "$ref": "#/components/headers/ApiVersion"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "ServerError": {
        "description": "Internal server error",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "API-Version": {
            "$ref": "#/components/headers/ApiVersion"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "No endpoint at this path",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "API-Version": {
            "$ref": "#/components/headers/ApiVersion"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/contact": {
      "post": {
        "operationId": "submitContact",
        "summary": "Submit a contact / project inquiry",
        "tags": [
          "Leads"
        ],
        "description": "Accepts a project inquiry. Required: name, email, message. Optional: company, service. Rate limit: 5 requests per minute per IP. Also accepts application/x-www-form-urlencoded (HTML form posts), which receive a 303 redirect instead of JSON.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email",
                  "message"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Sender's name"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Reply-to email address"
                  },
                  "message": {
                    "type": "string",
                    "description": "Inquiry message"
                  },
                  "company": {
                    "type": "string",
                    "description": "Company name (optional)"
                  },
                  "service": {
                    "type": "string",
                    "description": "Service of interest, e.g. seo, local-seo, ecommerce, ai-search-optimization, web-development, automation (optional)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submission accepted",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "API-Version": {
                "$ref": "#/components/headers/ApiVersion"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/newsletter": {
      "post": {
        "operationId": "subscribeNewsletter",
        "summary": "Subscribe to the newsletter",
        "tags": [
          "Leads"
        ],
        "description": "Adds an email address to the newsletter. Rate limit: 5 requests per minute per IP. Also accepts application/x-www-form-urlencoded (HTML form posts), which receive a 303 redirect instead of JSON.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Subscriber email address"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscription accepted",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "API-Version": {
                "$ref": "#/components/headers/ApiVersion"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/services": {
      "get": {
        "operationId": "listServices",
        "summary": "List services with pricing tiers",
        "tags": [
          "Services"
        ],
        "description": "Returns active services and their pricing tiers. Requires a signed-in member or admin session. Rate limit: 60 requests per minute per IP.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "Service list",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "API-Version": {
                "$ref": "#/components/headers/ApiVersion"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Service"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Service health",
        "tags": [
          "Meta"
        ],
        "responses": {
          "200": {
            "description": "Service is up",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "API-Version": {
                "$ref": "#/components/headers/ApiVersion"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "mcpJsonRpc",
        "summary": "MCP JSON-RPC endpoint",
        "tags": [
          "Agents"
        ],
        "description": "Model Context Protocol over streamable HTTP. Methods: initialize, tools/list, tools/call (submit_contact, list_insights). See /.well-known/mcp/server-card.json.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "API-Version": {
                "$ref": "#/components/headers/ApiVersion"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  }
}
