{
  "openapi": "3.1.0",
  "info": {
    "title": "ymrtech.com public HTTP surface",
    "version": "1.0.0",
    "summary": "The unauthenticated endpoints this site's own pages use.",
    "description": "Covers the health check, the contact form's token and submit routes, and the same-origin telemetry beacon. Everything under /api/* is the authenticated client portal and is out of scope. Human-readable version: https://ymrtech.com/docs/api/",
    "contact": {
      "name": "YMRTECH",
      "email": "yannick@ymrtech.com",
      "url": "https://ymrtech.com/contact/"
    },
    "license": {
      "name": "Site content - see terms",
      "identifier": "LicenseRef-Proprietary",
      "url": "https://ymrtech.com/terms/"
    }
  },
  "servers": [
    {
      "url": "https://ymrtech.com",
      "description": "Production"
    }
  ],
  "security": [],
  "paths": {
    "/healthz": {
      "get": {
        "operationId": "healthz",
        "summary": "Liveness probe",
        "description": "Answers 200 with the plain-text body `ok` when the vhost is serving. Cheap, unauthenticated, nothing to parse.",
        "responses": {
          "200": {
            "description": "Serving.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "const": "ok"
                }
              }
            }
          }
        }
      }
    },
    "/contact/token.json": {
      "get": {
        "operationId": "contactToken",
        "summary": "Token for the contact form",
        "description": "Returns the token the contact form sends as `X-Contact-Token`. It ships with the site and is not a secret: it exists so tokenless bots are rejected before they consume a real visitor's rate-limit budget.",
        "responses": {
          "200": {
            "description": "The current token.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "token"
                  ],
                  "properties": {
                    "token": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/contact/submit": {
      "post": {
        "operationId": "contactSubmit",
        "summary": "Send a message to the practice",
        "description": "Accepts the contact form as `application/x-www-form-urlencoded` or `multipart/form-data`. Requires `X-Contact-Token` matching /contact/token.json. Fields `name`, `email` and `message` are required; `company`, `service` and `budget` are optional. The hidden `website` field is a bot trap and must be left empty. Errors are JSON carrying an `error` string.",
        "parameters": [
          {
            "name": "X-Contact-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email",
                  "message"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "company": {
                    "type": "string"
                  },
                  "service": {
                    "type": "string"
                  },
                  "budget": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "website": {
                    "type": "string",
                    "description": "Bot trap. Must be empty.",
                    "maxLength": 0
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email",
                  "message"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "company": {
                    "type": "string"
                  },
                  "service": {
                    "type": "string"
                  },
                  "budget": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "website": {
                    "type": "string",
                    "description": "Bot trap. Must be empty.",
                    "maxLength": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Handed to the mail relay; the form then follows to /contact/thanks/."
          },
          "400": {
            "description": "Missing or malformed fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or wrong token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body larger than the accepted maximum.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported content type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Per-IP rate limit exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/vpn/rum/collect": {
      "post": {
        "operationId": "rumCollect",
        "summary": "Same-origin telemetry beacon (not a third-party API)",
        "description": "Receives the cookieless, first-party RUM client's page-view events. Same-origin by design and changes with the client; it is here because the site's own pages use it, not as an integration point.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A Faro transport envelope; see assets/js/vendor/README.md."
              }
            },
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted; queued for the local collector."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          }
        }
      }
    }
  }
}
