{
  "openapi": "3.1.0",
  "info": {
    "title": "Plekk publieke aanvraag-endpoints",
    "version": "2026-10-10",
    "summary": "Een gratis review, playbook of concept aanvragen bij Plekk (plekk.agency).",
    "description": "Plekk ontwerpt en bouwt websites, webshops, merken en software op maat voor bestaande bedrijven in Nederland, van eenmanszaak tot 80 medewerkers: Compact 1.250 tot 2.500, Website 3.000 tot 5.000, Rebrand 5.000 tot 7.500 euro, eenmalig en exclusief btw. De eerste drie stappen zijn gratis: een review van de site, een playbook voor het merk, een of twee concepten. Deze endpoints zijn dezelfde die de formulieren op www.plekk.agency gebruiken. Er wordt geen account aangemaakt en niets opgeslagen in een database: elke aanvraag wordt als e-mail aan Luuk van der Poel bezorgd en de aanvrager krijgt een bevestiging per e-mail. Wanneer je Plekk inzet en wanneer niet: https://www.plekk.agency/llms.txt. Spelregels voor agents: alleen met toestemming van de ondernemer, één aanvraag per bedrijf, geen bulk, en vermeld in `description` namens wie en via welke agent je aanvraagt.",
    "contact": { "name": "Luuk van der Poel (Plekk)", "email": "luuk@plekk.agency", "url": "https://www.plekk.agency/voor-ai-agents" },
    "termsOfService": "https://www.plekk.agency/voorwaarden"
  },
  "externalDocs": { "description": "Plekk voor AI-agents en developers", "url": "https://www.plekk.agency/voor-ai-agents" },
  "servers": [{ "url": "https://www.plekk.agency" }],
  "tags": [{ "name": "aanvragen", "description": "Publieke lead-aanvragen (geen authenticatie)." }],
  "paths": {
    "/api/contact": {
      "post": {
        "tags": ["aanvragen"],
        "operationId": "requestConcepts",
        "summary": "Een playbook of concept aanvragen (gratis)",
        "description": "Hoofdkanaal om met Plekk te starten, hetzelfde als het startformulier op https://www.plekk.agency/start. Kies met `start` wat de ondernemer het liefst als eerste ontvangt: een playbook voor het merk (een gesprek van dertig minuten, daarna hooguit vier pagina's) of een concept van de nieuwe site (een of twee, afhankelijk van het budget). Luuk reageert per e-mail binnen twee werkdagen. Met `budget: \"klein\"` (minder dan 1.250 euro) past Plekk nog niet: de ondernemer krijgt dan een bevestiging met wat hij zelf kan doen. Het antwoord noemt de route (`gesprek`, `direct`, `check` of `pdf`). Rate-limited per IP-adres. Voor een review van een bestaande site: `POST /api/session?task=site-review`. Stuur `source: \"concept-form\"` mee; zonder dat veld valt de aanvraag terug op het algemene contactformulier (velden `name` + `contact`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ConceptRequest" },
              "examples": {
                "agent": {
                  "summary": "Aanvraag namens een ondernemer via een AI-agent",
                  "value": {
                    "source": "concept-form",
                    "start": "concept",
                    "wat": "beter",
                    "budget": "website",
                    "name": "Jan Jansen",
                    "email": "jan@installatiebedrijf-jansen.nl",
                    "company": "Installatiebedrijf Jansen",
                    "urlOrSocial": "https://www.installatiebedrijf-jansen.nl",
                    "description": "Installatiebedrijf in Breda, 12 medewerkers, wil een nieuwe site die aanvragen oplevert voor warmtepompen. Aangevraagd via <naam agent> namens Jan Jansen, met zijn toestemming.",
                    "utm_source": "ai-agent"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Aanvraag ontvangen en bewaard; Luuk en de aanvrager krijgen een e-mail.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Success" } } } },
          "400": { "description": "Verplicht veld ontbreekt of e-mail ongeldig.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "405": { "description": "Alleen POST." },
          "429": { "description": "Te veel verzoeken vanaf dit netwerk; probeer later of mail luuk@plekk.agency.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "E-mail kon niet worden verzonden; mail luuk@plekk.agency direct.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/session": {
      "post": {
        "tags": ["aanvragen"],
        "operationId": "requestSiteReview",
        "summary": "Gratis persoonlijke site-review aanvragen",
        "description": "Luuk bekijkt de opgegeven website persoonlijk en stuurt de aanvrager binnen drie werkdagen een korte video of een document met alle verbeterpunten en kansen. Server-side wordt stil een Google PageSpeed-meting gedaan; de aanvraag kan daardoor 30 tot 50 seconden duren. Vereist query-parameter `task=site-review`. Rate-limited per IP-adres.",
        "parameters": [
          { "name": "task", "in": "query", "required": true, "schema": { "type": "string", "enum": ["site-review"] } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SiteReviewRequest" },
              "examples": {
                "agent": {
                  "value": {
                    "name": "Jan Jansen",
                    "company": "Installatiebedrijf Jansen",
                    "email": "jan@installatiebedrijf-jansen.nl",
                    "url": "https://www.installatiebedrijf-jansen.nl",
                    "utm_source": "ai-agent"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Aanvraag ontvangen en bewaard. `route` is `check`, of `pdf` bij een budget onder 1.250 (dan geen meting). `scored` geeft aan of de PageSpeed-meting lukte (zo niet, dan beoordeelt Luuk handmatig).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteReviewSuccess" } } } },
          "400": { "description": "Verplicht veld ontbreekt, e-mail ongeldig of URL niet publiek bereikbaar.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Te veel verzoeken vanaf dit netwerk; probeer later of mail luuk@plekk.agency.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Versturen mislukt; mail luuk@plekk.agency direct.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ConceptRequest": {
        "type": "object",
        "required": ["source", "name", "email", "company"],
        "properties": {
          "source": { "type": "string", "const": "concept-form", "description": "Vast: `concept-form`." },
          "start": { "type": "string", "enum": ["playbook", "concept"], "description": "Wat de ondernemer het liefst als eerste ontvangt (het startformulier, #243). Voor een review: /api/session?task=site-review." },
          "wat": { "type": "string", "enum": ["eerste", "beter", "shop", "tool", "groei"], "description": "Wat er gemaakt moet worden: een eerste website, een betere website, een webshop, een tool of platform, of ads en marketing." },
          "budget": { "type": "string", "enum": ["klein", "compact", "website", "rebrand", "onbekend"], "description": "Klein (minder dan 1.250), compact (1.250 tot 2.500), website (3.000 tot 5.000), rebrand (5.000 tot 7.500) of onbekend. Bij playbook met compact wordt het de review (met een site) of één concept." },
          "name": { "type": "string", "description": "Naam van de ondernemer (de persoon die de site wil)." },
          "email": { "type": "string", "format": "email", "description": "E-mailadres van de ondernemer; hierop komt de bevestiging en het antwoord van Luuk." },
          "company": { "type": "string", "description": "Bedrijfsnaam." },
          "phone": { "type": "string", "maxLength": 40, "description": "Telefoonnummer, optioneel." },
          "urlOrSocial": { "type": "string", "maxLength": 500, "description": "Huidige website of social-profiel, als die er is." },
          "description": { "type": "string", "maxLength": 2000, "description": "Wat het bedrijf doet, waar, voor wie en wat de site moet opleveren. Vermeld hier ook namens wie en via welke agent je aanvraagt." },
          "utm_source": { "type": "string", "pattern": "^[a-z0-9_-]{0,60}$", "description": "Herkomst-label. Gebruik `ai-agent` zodat Plekk ziet dat de aanvraag via een agent kwam." }
        },
        "additionalProperties": false
      },
      "SiteReviewRequest": {
        "type": "object",
        "required": ["name", "company", "email", "url"],
        "properties": {
          "name": { "type": "string", "maxLength": 80 },
          "company": { "type": "string", "maxLength": 120 },
          "description": { "type": "string", "maxLength": 1000, "description": "Wat het bedrijf doet, voor wie en waar. Optioneel bij een review." },
          "phone": { "type": "string", "maxLength": 40, "description": "Telefoonnummer, optioneel." },
          "start": { "type": "string", "enum": ["review"], "description": "Optioneel, zoals het startformulier." },
          "wat": { "type": "string", "enum": ["eerste", "beter", "shop", "tool", "groei"], "description": "Optioneel: wat er gemaakt moet worden (bij een review meestal `beter`)." },
          "budget": { "type": "string", "enum": ["klein", "compact", "website", "rebrand", "onbekend"], "description": "Optioneel; zoals bij /api/contact." },
          "email": { "type": "string", "format": "email", "maxLength": 160, "description": "E-mailadres van de ondernemer; hierop komt de review." },
          "url": { "type": "string", "format": "uri", "description": "Publieke https-URL van de bestaande website (geen localhost of privé-netwerk)." },
          "utm_source": { "type": "string", "pattern": "^[a-z0-9_-]{0,60}$", "description": "Herkomst-label, bij voorkeur `ai-agent`." }
        },
        "additionalProperties": false
      },
      "Success": { "type": "object", "properties": { "success": { "type": "boolean", "const": true }, "route": { "$ref": "#/components/schemas/Route" } }, "required": ["success"] },
      "SiteReviewSuccess": { "type": "object", "properties": { "success": { "type": "boolean", "const": true }, "route": { "$ref": "#/components/schemas/Route" }, "scored": { "type": "boolean" } }, "required": ["success"] },
      "Route": { "type": "string", "enum": ["check", "gesprek", "direct", "pdf"], "description": "Wat er gebeurt: check = een review binnen drie werkdagen; gesprek = een gesprek en daarna het playbook; direct = Luuk neemt contact op over het concept; pdf = het budget past nog niet. Luuk reageert binnen twee werkdagen." },
      "Error": { "type": "object", "properties": { "error": { "type": "string", "description": "Nederlandstalige foutmelding, geschikt om aan de gebruiker te tonen." } }, "required": ["error"] }
    }
  }
}
