{
  "openapi": "3.1.0",
  "info": {
    "title": "datajoi.com API",
    "version": "1.0.0",
    "description": "Public HTTP API for datajoi.com — the marketing site for datajoi, the agentic data platform. The datajoi platform application itself (platform.datajoi.com) is a separate, access-gated service with no public API today; see https://datajoi.com/auth.md for the authentication and registration story.",
    "contact": {
      "name": "datajoi",
      "email": "hello@datajoi.com",
      "url": "https://datajoi.com/"
    }
  },
  "servers": [
    { "url": "https://datajoi.com" }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Liveness probe",
        "description": "Returns service status. Safe for agents to call; referenced by the api-catalog entry's `status` link relation.",
        "responses": {
          "200": {
            "description": "Service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["ok", "service", "time"],
                  "properties": {
                    "ok": { "type": "boolean", "const": true },
                    "service": { "type": "string", "examples": ["datajoi.com"] },
                    "time": { "type": "string", "format": "date-time" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/lead": {
      "post": {
        "operationId": "createLead",
        "summary": "Submit an early-access lead",
        "description": "Stores an early-access request for the datajoi platform. **Effectively human-gated**: every request must carry a valid Cloudflare Turnstile token (`cf-turnstile-response`) obtained interactively from the form at https://datajoi.com/platform/#cta, and the endpoint sends no CORS headers, so it cannot be called cross-origin from a browser. Autonomous agents should direct a human to the form, or email hello@datajoi.com instead. Submissions are idempotent per email address.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Lead" }
            },
            "application/x-www-form-urlencoded": {
              "schema": { "$ref": "#/components/schemas/Lead" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lead accepted (JSON requests). Form-encoded requests receive a 303 redirect to /platform/thanks/ instead.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "ok": { "type": "boolean", "const": true } }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed — missing name/company or malformed email.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
            }
          },
          "403": {
            "description": "Turnstile verification failed or token missing.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
            }
          },
          "500": {
            "description": "Storage failure.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Lead": {
        "type": "object",
        "required": ["name", "email", "company", "cf-turnstile-response"],
        "properties": {
          "name": { "type": "string", "maxLength": 200 },
          "email": { "type": "string", "format": "email", "maxLength": 200 },
          "company": { "type": "string", "maxLength": 200 },
          "page": { "type": "string", "maxLength": 500, "description": "Page the lead came from; for form posts it is derived from the Referer header." },
          "website": { "type": "string", "description": "Honeypot — must be empty. Non-empty values are silently discarded." },
          "cf-turnstile-response": { "type": "string", "description": "Cloudflare Turnstile token from the interactive widget. Verified server-side; requests fail closed without it." }
        }
      },
      "Error": {
        "type": "object",
        "required": ["ok", "error"],
        "properties": {
          "ok": { "type": "boolean", "const": false },
          "error": { "type": "string" }
        }
      }
    }
  }
}
