Skip to main content
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

{
  "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
    }
  ]
}

Prompt snippet

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:
{
  "status": "success",
  "data": {
    "carrier": "ups",
    "service": "Ground",
    "estimated_days": 4
  }
}
Error:
{
  "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.