{
  "openapi": "3.1.0",
  "info": {
    "title": "Moveezi public site API",
    "version": "1.0.0",
    "summary": "Read moveezi.com as structured data, and start a sales conversation.",
    "description": "Public, unauthenticated, read-only access to the content of moveezi.com, plus the same lead endpoint the website's own forms use.\n\nMoveezi is the operating system for moving, relocation and storage companies: sales and quoting, surveys, dispatch and crew scheduling, a mobile crew app, warehousing, shipment tracking, a live control tower and per-job P&L.\n\n**When to use this API.** Reach for it to answer questions about what Moveezi does, which segments it serves (local, interstate, international, storage, RMC, agent, military and government), what it costs, how it handles security and compliance, or to pass a qualified enquiry to the Moveezi sales team. It does not expose customer or operational data; the tenant API behind app.moveezi.com is authenticated and separate.\n\n**Limits.** Read endpoints are unmetered and cacheable for five minutes. `POST /api/lead` is limited to 5 submissions per IP per hour and replies 429 beyond that.\n\nEvery page is also available as markdown at `<url>.md`, or from any page URL with `Accept: text/markdown` ([acceptmarkdown.com](https://acceptmarkdown.com/)). There is an MCP server over Streamable HTTP at `/mcp`.\n\n**Versioning and deprecation.** The version is in the path (`/api/v1`). Within a major version this API only ever changes additively: new endpoints, new optional parameters, new response fields. Nothing is removed or renamed without notice. When an operation is to be withdrawn, its responses carry `Deprecation: <date>` and `Sunset: <date>` (RFC 8594 and RFC 9745) and a `Link` with `rel=\"deprecation\"` pointing at the replacement, for at least six months before it goes, and the operation is marked `deprecated: true` in this document. No endpoint is deprecated today. Authentication is described at `/auth.md`: there is none, and that is not going to change for the public API.\n\n**Clients.** Official JavaScript and Python SDKs and a CLI: https://github.com/DumontAI/moveezi-sdk (`pip install moveezi`, or `npm install DumontAI/moveezi-sdk` for the JavaScript one).\n\n**Sandbox.** `POST /api/v1/lead/validate` runs a lead payload through the same rules as `POST /api/lead` and writes nothing, so an agent can check a submission before creating a real record.",
    "contact": {
      "name": "Moveezi",
      "email": "sales@moveezi.com",
      "url": "https://www.moveezi.com/developers"
    },
    "license": {
      "name": "Site content (c) Dumont Pty Ltd, all rights reserved",
      "identifier": "LicenseRef-Proprietary"
    },
    "termsOfService": "https://www.moveezi.com/terms"
  },
  "servers": [
    {
      "url": "https://www.moveezi.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "url": "https://www.moveezi.com/developers",
    "description": "Developer portal: API, markdown and MCP"
  },
  "x-deprecation-policy": "https://www.moveezi.com/api-deprecation-policy",
  "x-sandbox": "https://www.moveezi.com/api/v1/lead/validate",
  "tags": [
    {
      "name": "content",
      "description": "Read the public website as data."
    },
    {
      "name": "sales",
      "description": "Reach a human at Moveezi."
    }
  ],
  "paths": {
    "/api/v1/pages": {
      "get": {
        "operationId": "listPages",
        "tags": [
          "content"
        ],
        "summary": "List every published page",
        "description": "The page index of moveezi.com, without bodies. Use it to find a slug for getPage. Cursor paginated: pass the `nextCursor` from the previous response back as `cursor` until it is absent.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum pages to return.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` from the previous response. It is the last slug returned, so a page added between calls cannot make you skip or repeat one.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "prefix",
            "in": "query",
            "required": false,
            "description": "Only pages whose slug starts with this, e.g. `blog/`.",
            "schema": {
              "type": "string",
              "examples": [
                "blog/"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the page index.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "count",
                    "pages"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of pages returned."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Pass back as `cursor` for the next page. Absent on the last page."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PageSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Back off for `Retry-After` seconds.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pages/{slug}": {
      "get": {
        "operationId": "getPage",
        "tags": [
          "content"
        ],
        "summary": "Read one page",
        "description": "One page of moveezi.com as markdown plus metadata. Send `Accept: text/markdown` to get the markdown document on its own.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Page identifier, e.g. `pricing`, `features`, `index` for the home page, or `blog/<post>` for a blog post. List them with getPages.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9/-]+$",
              "examples": [
                "pricing",
                "features",
                "index"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Back off for `Retry-After` seconds.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "searchSite",
        "tags": [
          "content"
        ],
        "summary": "Search the website",
        "description": "Full-text search across every published page. Results are ranked, with a snippet around the first match.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search terms. All terms must appear.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "examples": [
                "warehouse billing"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum results to return.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked matches.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "query",
                    "count",
                    "results"
                  ],
                  "properties": {
                    "query": {
                      "type": "string",
                      "description": "Echo of q."
                    },
                    "count": {
                      "type": "integer",
                      "description": "Number of results returned."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SearchResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or too-short q.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Back off for `Retry-After` seconds.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pages/batch": {
      "post": {
        "operationId": "getPagesBatch",
        "tags": [
          "content"
        ],
        "summary": "Read many pages in one request",
        "description": "Up to 50 pages in a single round trip, so an agent acting on a list does not loop getPage. Missing slugs come back marked, not as an error.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slugs"
                ],
                "properties": {
                  "slugs": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "description": "Page slugs, from listPages.",
                    "items": {
                      "type": "string"
                    },
                    "examples": [
                      [
                        "pricing",
                        "features",
                        "security"
                      ]
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result per requested slug, in order.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "count",
                    "results"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of results."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "slug",
                          "found"
                        ],
                        "properties": {
                          "slug": {
                            "type": "string",
                            "description": "Echo of the slug."
                          },
                          "found": {
                            "type": "boolean",
                            "description": "Whether it exists."
                          },
                          "page": {
                            "$ref": "#/components/schemas/Page"
                          },
                          "error": {
                            "type": "string",
                            "description": "Why it was not returned."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "slugs missing, empty, or over 50.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Back off for `Retry-After` seconds.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/lead/validate": {
      "post": {
        "operationId": "validateLead",
        "tags": [
          "sales"
        ],
        "summary": "Sandbox: check a lead without submitting it",
        "description": "Runs the same validation as submitLead and writes nothing: no CRM record, no email, no rate-limit budget on the lead endpoint. Use it before submitLead so a real enquiry is never rejected or silently truncated.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Lead"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The payload would be accepted.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeadValidation"
                }
              }
            }
          },
          "422": {
            "description": "The payload would be rejected or truncated. Nothing was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeadValidation"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Back off for `Retry-After` seconds.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/lead": {
      "post": {
        "operationId": "submitLead",
        "tags": [
          "sales"
        ],
        "summary": "Send an enquiry to the Moveezi sales team",
        "description": "Creates a lead in Moveezi's CRM and notifies the sales team. This is the same endpoint the website's own contact and demo forms post to. Rate limited to 5 per IP per hour. Only submit on behalf of a person who asked you to.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "A unique key for this submission. Retrying with the same key replays the first response instead of creating a second lead, for 24 hours, and does not consume rate-limit budget. A replay is marked with `Idempotency-Replayed: true`.",
            "schema": {
              "type": "string",
              "maxLength": 200,
              "examples": [
                "a3f1c9e2-7b04-4a1d-9f2e-1c6b8d0a5e33"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Lead"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lead accepted.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Submissions allowed per hour per IP.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Submissions left in the window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window frees a slot.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy, e.g. `5;w=3600`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "True when the lead was accepted."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing name or invalid email.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this IP. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window frees a slot.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Submissions allowed per hour per IP.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Lead could not be recorded; use the chat on the website instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Page": {
        "type": "object",
        "description": "One published page of moveezi.com.",
        "required": [
          "slug",
          "title",
          "url",
          "markdown"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Page identifier used by this API."
          },
          "title": {
            "type": "string",
            "description": "HTML document title."
          },
          "description": {
            "type": "string",
            "description": "Meta description."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical page URL."
          },
          "markdown_url": {
            "type": "string",
            "format": "uri",
            "description": "Same page as a standalone markdown document."
          },
          "updated": {
            "type": "string",
            "format": "date",
            "description": "Date the page was last published (UTC)."
          },
          "markdown": {
            "type": "string",
            "description": "Page body converted to markdown, navigation and footer chrome removed."
          }
        }
      },
      "PageSummary": {
        "type": "object",
        "description": "A page without its body.",
        "required": [
          "slug",
          "title",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Page identifier used by this API."
          },
          "title": {
            "type": "string",
            "description": "HTML document title."
          },
          "description": {
            "type": "string",
            "description": "Meta description."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical page URL."
          },
          "markdown_url": {
            "type": "string",
            "format": "uri",
            "description": "Same page as a standalone markdown document."
          },
          "updated": {
            "type": "string",
            "format": "date",
            "description": "Date the page was last published (UTC)."
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "description": "One matching page.",
        "required": [
          "slug",
          "title",
          "url",
          "snippet",
          "score"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Page identifier."
          },
          "title": {
            "type": "string",
            "description": "Page title."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical page URL."
          },
          "markdown_url": {
            "type": "string",
            "format": "uri",
            "description": "Markdown version of the page."
          },
          "snippet": {
            "type": "string",
            "description": "Text around the first match."
          },
          "score": {
            "type": "integer",
            "description": "Relevance; higher is better."
          }
        }
      },
      "Lead": {
        "type": "object",
        "description": "An enquiry from a prospective customer.",
        "required": [
          "name",
          "email"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120,
            "description": "Full name of the person enquiring."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 200,
            "description": "Their work email address."
          },
          "company": {
            "type": "string",
            "maxLength": 200,
            "description": "Company name."
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "description": "Phone number in local format; pair it with dialcode."
          },
          "dialcode": {
            "type": "string",
            "pattern": "^\\+\\d{1,4}$",
            "default": "+61",
            "description": "International calling code for phone."
          },
          "country": {
            "type": "string",
            "maxLength": 60,
            "description": "Country they operate in."
          },
          "message": {
            "type": "string",
            "maxLength": 4000,
            "description": "What they want to know."
          },
          "intent": {
            "type": "string",
            "enum": [
              "demo",
              "enquiry"
            ],
            "default": "enquiry",
            "description": "`demo` books a walkthrough; `enquiry` is a question."
          },
          "segment": {
            "type": "string",
            "enum": [
              "Local & interstate",
              "International",
              "Military & government",
              "Mixed"
            ],
            "description": "Kind of moving work they do."
          },
          "warehouses": {
            "type": "string",
            "enum": [
              "None",
              "1",
              "2-5",
              "6+"
            ],
            "description": "Number of warehouses they run."
          },
          "moves": {
            "type": "string",
            "enum": [
              "Under 500",
              "500-2,000",
              "2,000-10,000",
              "10,000+"
            ],
            "description": "Moves per year."
          }
        }
      },
      "LeadValidation": {
        "type": "object",
        "description": "What submitLead would do with this payload.",
        "required": [
          "sandbox",
          "wouldSubmit",
          "problems"
        ],
        "properties": {
          "sandbox": {
            "type": "boolean",
            "description": "Always true; nothing was written."
          },
          "wouldSubmit": {
            "type": "boolean",
            "description": "Whether submitLead would accept it."
          },
          "problems": {
            "type": "array",
            "description": "Empty when the payload is good.",
            "items": {
              "type": "object",
              "required": [
                "field",
                "error"
              ],
              "properties": {
                "field": {
                  "type": "string",
                  "description": "The offending field."
                },
                "error": {
                  "type": "string",
                  "description": "What is wrong with it."
                }
              }
            }
          },
          "note": {
            "type": "string",
            "description": "How to submit it for real."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable reason."
          },
          "status": {
            "type": "integer",
            "description": "Repeated HTTP status code."
          }
        }
      }
    }
  }
}
