> ## 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.

# Schema composition with anyOf

> Use discriminated branches to validate one of several alternative shapes.

Some fields only make sense in certain contexts. Requiring an email address when the user chose SMS delivery is noise; omitting a phone number when they chose SMS is a bug. A discriminated union encodes this cleanly: one branch for email, one branch for SMS.

## Use case

A checkout flow where `delivery_method` decides which contact details are mandatory. When the user wants email delivery, you need their email address. When they want SMS, you need their phone number. Both should never be required simultaneously.

## Schema pattern

<CodeGroup>
  ```json JSON Schema theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "anyOf": [
      {
        "type": "object",
        "properties": {
          "delivery_method": { "type": "string", "const": "email" },
          "email": { "type": "string", "pattern": "^[^@]+@[^@]+$" }
        },
        "required": ["delivery_method", "email"],
        "additionalProperties": false
      },
      {
        "type": "object",
        "properties": {
          "delivery_method": { "type": "string", "const": "sms" },
          "phone": { "type": "string", "pattern": "^\\+?[1-9][0-9]{7,14}$" }
        },
        "required": ["delivery_method", "phone"],
        "additionalProperties": false
      }
    ]
  }
  ```

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

  class EmailDelivery(BaseModel):
      model_config = ConfigDict(extra="forbid")
      delivery_method: Literal["email"]
      email: str = Field(..., pattern=r"^[^@]+@[^@]+$")

  class SmsDelivery(BaseModel):
      model_config = ConfigDict(extra="forbid")
      delivery_method: Literal["sms"]
      phone: str = Field(..., pattern=r"^\+?[1-9][0-9]{7,14}$")

  Delivery = Annotated[EmailDelivery | SmsDelivery, Field(discriminator="delivery_method")]
  ```

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

  const deliverySchema = z.discriminatedUnion("delivery_method", [
    z.object({
      delivery_method: z.literal("email"),
      email: z.string().regex(/^[^@]+@[^@]+$/),
    }).strict(),
    z.object({
      delivery_method: z.literal("sms"),
      phone: z.string().regex(/^\+?[1-9][0-9]{7,14}$/),
    }).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": "Send order confirmation to jane@acme.com via email." }],
      "response_format": {
        "type": "json_schema",
        "json_schema": {
          "name": "delivery_method",
          "schema": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "delivery_method": { "type": "string", "const": "email" },
                  "email": { "type": "string", "pattern": "^[^@]+@[^@]+$" }
                },
                "required": ["delivery_method", "email"],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "delivery_method": { "type": "string", "const": "sms" },
                  "phone": { "type": "string", "pattern": "^\\+?[1-9][0-9]{7,14}$" }
                },
                "required": ["delivery_method", "phone"],
                "additionalProperties": false
              }
            ]
          }
        }
      }
    }'
  ```
</CodeGroup>

## Prompt snippet

```text theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
Infer the delivery method from the user request.
If method is email, include email.
If method is sms, include phone in international format.
```

## Example outputs

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "delivery_method": "email",
  "email": "jane@acme.com"
}
```

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "delivery_method": "sms",
  "phone": "+14155551234"
}
```

## Why this works

The `anyOf` branches split the payload into two exact shapes. The email branch requires `email`, and the SMS branch requires `phone`. Because `delivery_method` is fixed with `const` in each branch, the model cannot mix fields across branches, so `anyOf` is equivalent to `oneOf` here.

## Related docs

* [Conditionals reference](/json-schema/reference/conditionals)
* [String reference](/json-schema/reference/string)
