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

# Success and Error Output

> Use a single response envelope that stays parseable in both success and failure paths.

When a model can either succeed or fail at a task, you need both outcomes to be parseable by the same code. If success returns `{"carrier": "ups", ...}` and failure returns `{"error": "missing zip"}`, every consumer needs two parsers and two code paths. A response envelope solves this: wrap both outcomes in a discriminator union keyed by `status`, and require the matching payload in each branch.

## Use case

You are extracting shipping options from user text. Sometimes extraction succeeds, sometimes required details are missing. Your API client should always parse the same top-level keys regardless of outcome.

## Schema pattern

<CodeGroup>
  ```json JSON Schema theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "anyOf": [
      {
        "type": "object",
        "properties": {
          "status": { "type": "string", "const": "success" },
          "data": {
            "type": "object",
            "properties": {
              "carrier": { "type": "string", "enum": ["ups", "fedex", "dhl"] },
              "service": { "type": "string", "minLength": 1, "maxLength": 40 },
              "estimated_days": { "type": "integer", "minimum": 1, "maximum": 30 }
            },
            "required": ["carrier", "service", "estimated_days"],
            "additionalProperties": false
          }
        },
        "required": ["status", "data"],
        "additionalProperties": false
      },
      {
        "type": "object",
        "properties": {
          "status": { "type": "string", "const": "error" },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": ["missing_input", "ambiguous_request", "unsupported_region"]
              },
              "message": { "type": "string", "minLength": 1, "maxLength": 200 },
              "retryable": { "type": "boolean" }
            },
            "required": ["code", "message", "retryable"],
            "additionalProperties": false
          }
        },
        "required": ["status", "error"],
        "additionalProperties": false
      }
    ]
  }
  ```

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

  class ShippingData(BaseModel):
      model_config = ConfigDict(extra="forbid")
      carrier: Literal["ups", "fedex", "dhl"]
      service: str = Field(..., min_length=1, max_length=40)
      estimated_days: int = Field(..., ge=1, le=30)

  class ErrorData(BaseModel):
      model_config = ConfigDict(extra="forbid")
      code: Literal["missing_input", "ambiguous_request", "unsupported_region"]
      message: str = Field(..., min_length=1, max_length=200)
      retryable: bool

  class SuccessResponse(BaseModel):
      model_config = ConfigDict(extra="forbid")
      status: Literal["success"]
      data: ShippingData

  class ErrorResponse(BaseModel):
      model_config = ConfigDict(extra="forbid")
      status: Literal["error"]
      error: ErrorData

  Response = Annotated[SuccessResponse | ErrorResponse, Field(discriminator="status")]
  ```

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

  const responseSchema = z.discriminatedUnion("status", [
    z.object({
      status: z.literal("success"),
      data: z.object({
        carrier: z.enum(["ups", "fedex", "dhl"]),
        service: z.string().min(1).max(40),
        estimated_days: z.number().int().min(1).max(30),
      }).strict(),
    }).strict(),
    z.object({
      status: z.literal("error"),
      error: z.object({
        code: z.enum(["missing_input", "ambiguous_request", "unsupported_region"]),
        message: z.string().min(1).max(200),
        retryable: z.boolean(),
      }).strict(),
    }).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 via UPS Ground to New York, estimated 4 days." }],
      "response_format": {
        "type": "json_schema",
        "json_schema": {
          "name": "shipping_response",
          "schema": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "status": { "type": "string", "const": "success" },
                  "data": {
                    "type": "object",
                    "properties": {
                      "carrier": { "type": "string", "enum": ["ups", "fedex", "dhl"] },
                      "service": { "type": "string", "minLength": 1, "maxLength": 40 },
                      "estimated_days": { "type": "integer", "minimum": 1, "maximum": 30 }
                    },
                    "required": ["carrier", "service", "estimated_days"],
                    "additionalProperties": false
                  }
                },
                "required": ["status", "data"],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "status": { "type": "string", "const": "error" },
                  "error": {
                    "type": "object",
                    "properties": {
                      "code": { "type": "string", "enum": ["missing_input", "ambiguous_request", "unsupported_region"] },
                      "message": { "type": "string", "minLength": 1, "maxLength": 200 },
                      "retryable": { "type": "boolean" }
                    },
                    "required": ["code", "message", "retryable"],
                    "additionalProperties": false
                  }
                },
                "required": ["status", "error"],
                "additionalProperties": false
              }
            ]
          }
        }
      }
    }'
  ```
</CodeGroup>

## Prompt snippet

```text theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
If you can determine a shipping option, set status="success" and fill data.
If key details are missing, set status="error" and fill error.
Never return both data and error as empty.
```

## Example outputs

Success:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "status": "success",
  "data": {
    "carrier": "ups",
    "service": "Ground",
    "estimated_days": 4
  }
}
```

Error:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "status": "error",
  "error": {
    "code": "missing_input",
    "message": "Destination ZIP code is missing.",
    "retryable": true
  }
}
```

## Why this works

Every consumer starts by reading `status` and branching on its value. Because each `anyOf` branch fixes `status` with `const`, there is no ambiguity about which fields are valid next.

The `anyOf` union still enforces the right shape here because the two branches are mutually exclusive: `status: "success"` requires `data`, and `status: "error"` requires `error`. This means your application never receives a success response with missing data, or an error response without an error code.

## Related docs

* [Object reference](/json-schema/reference/object)
* [Conditionals reference](/json-schema/reference/conditionals)
* [Composition reference](/json-schema/reference/composition)
