> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dottxt.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Form Processing

> Normalize unstructured form text into a strict backend payload with validation-friendly fields.

Users type addresses with inconsistent formatting, phone numbers with or without country codes, and shipping preferences in plain English. If you pass this text to your backend as-is, you need normalization logic, validation logic, and error handling for every possible format variation. Schema-constrained generation does this normalization at extraction time: the model reads the messy input and produces a clean, typed object that conforms to your backend's expectations.

## Goal

Convert mixed-quality user submissions into a normalized order payload with correct types, validated formats, and consistent structure, ready for your order creation API.

## Schema contract

<CodeGroup>
  ```json JSON Schema theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "type": "object",
    "properties": {
      "customer": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "minLength": 1, "maxLength": 100 },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": ["string", "null"], "pattern": "^\\+?[1-9][0-9]{7,14}$" }
        },
        "required": ["name", "email", "phone"],
        "additionalProperties": false
      },
      "shipping": {
        "type": "object",
        "properties": {
          "method": { "type": "string", "enum": ["standard", "express"] },
          "address_line1": { "type": "string", "minLength": 1, "maxLength": 120 },
          "city": { "type": "string", "minLength": 1, "maxLength": 80 },
          "postal_code": { "type": "string", "minLength": 3, "maxLength": 20 },
          "country": { "type": "string", "pattern": "^[A-Z]{2}$" }
        },
        "required": ["method", "address_line1", "city", "postal_code", "country"],
        "additionalProperties": false
      },
      "notes": { "type": ["string", "null"], "maxLength": 300 }
    },
    "required": ["customer", "shipping"],
    "additionalProperties": false
  }
  ```

  ```python Pydantic theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  from typing import Literal
  from pydantic import BaseModel, ConfigDict, EmailStr, Field

  class Customer(BaseModel):
      model_config = ConfigDict(extra="forbid")
      name: str = Field(..., min_length=1, max_length=100)
      email: EmailStr
      phone: str | None = Field(..., pattern=r"^\+?[1-9][0-9]{7,14}$")

  class Shipping(BaseModel):
      model_config = ConfigDict(extra="forbid")
      method: Literal["standard", "express"]
      address_line1: str = Field(..., min_length=1, max_length=120)
      city: str = Field(..., min_length=1, max_length=80)
      postal_code: str = Field(..., min_length=3, max_length=20)
      country: str = Field(..., pattern=r"^[A-Z]{2}$")

  class OrderPayload(BaseModel):
      model_config = ConfigDict(extra="forbid")
      customer: Customer
      shipping: Shipping
      notes: str | None = Field(None, max_length=300)
  ```

  ```typescript Zod theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  import { z } from "zod";

  const orderPayloadSchema = z.object({
    customer: z.object({
      name: z.string().min(1).max(100),
      email: z.string().email(),
      phone: z.string().regex(/^\+?[1-9][0-9]{7,14}$/).nullable(),
    }).strict(),
    shipping: z.object({
      method: z.enum(["standard", "express"]),
      address_line1: z.string().min(1).max(120),
      city: z.string().min(1).max(80),
      postal_code: z.string().min(3).max(20),
      country: z.string().regex(/^[A-Z]{2}$/),
    }).strict(),
    notes: z.string().max(300).nullable().optional(),
  }).strict();
  ```

  ```bash curl theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  curl https://api.dottxt.ai/v1/chat/completions \
    -H "Authorization: Bearer $DOTTXT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "openai/gpt-oss-20b",
      "messages": [{ "role": "user", "content": "Ship this to 10 Main Street, Austin 78701 US. Use express please. Name: Alice Johnson, email alice@acme.com. No phone." }],
      "response_format": {
        "type": "json_schema",
        "json_schema": {
          "name": "order_payload",
          "schema": {
            "type": "object",
            "properties": {
              "customer": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "minLength": 1, "maxLength": 100 },
                  "email": { "type": "string", "format": "email" },
                  "phone": { "type": ["string", "null"], "pattern": "^\\+?[1-9][0-9]{7,14}$" }
                },
                "required": ["name", "email", "phone"],
                "additionalProperties": false
              },
              "shipping": {
                "type": "object",
                "properties": {
                  "method": { "type": "string", "enum": ["standard", "express"] },
                  "address_line1": { "type": "string", "minLength": 1, "maxLength": 120 },
                  "city": { "type": "string", "minLength": 1, "maxLength": 80 },
                  "postal_code": { "type": "string", "minLength": 3, "maxLength": 20 },
                  "country": { "type": "string", "pattern": "^[A-Z]{2}$" }
                },
                "required": ["method", "address_line1", "city", "postal_code", "country"],
                "additionalProperties": false
              },
              "notes": { "type": ["string", "null"], "maxLength": 300 }
            },
            "required": ["customer", "shipping"],
            "additionalProperties": false
          }
        }
      }
    }'
  ```
</CodeGroup>

## Example input

```text theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
Ship this to 10 Main Street, Austin 78701 US.
Use express please.
I'm Alice Johnson, email alice@acme.com. No phone.
```

## Example output

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "customer": {
    "name": "Alice Johnson",
    "email": "alice@acme.com",
    "phone": null
  },
  "shipping": {
    "method": "express",
    "address_line1": "10 Main Street",
    "city": "Austin",
    "postal_code": "78701",
    "country": "US"
  }
}
```

## Implementation tips

* **Nullable for "asked but absent."** The user explicitly said "no phone," so `phone` is `null` rather than omitted. Your backend can distinguish "no phone provided" from "phone not asked," which is useful for follow-up workflows.
* **Enums for controlled vocabulary.** `method` is `"standard"` or `"express"`, not `"fast"`, `"next day"`, or `"ASAP"`. The enum forces normalization so your shipping logic doesn't need string matching.
* **Pattern for format enforcement.** The `country` field uses `"^[A-Z]{2}$"` so the model produces `"US"` instead of `"United States"`, `"usa"`, or `"U.S.A."`. The same idea applies to `phone`: the E.164-like pattern ensures a format your telephony API accepts.
* **`additionalProperties: false`** prevents the model from forwarding raw user text as extra fields, which could leak PII into systems that don't expect it.

## Related docs

* [Optional vs Null](/json-schema/optional-vs-null): distinguish "not provided" from "explicitly absent"
* [Optional fields](/json-schema/optional-fields): fields the model can omit entirely
* [String bounds](/json-schema/string-bounds): control length, format, and regex on form fields
* [Object reference](/json-schema/reference/object) | [String reference](/json-schema/reference/string)
