Conditional outputs
Use if, then and else when one generated value determines what else the answer may contain. An unsuccessful operation can require an error code; a successful one can forbid it.
These are JSON Schema conditions on the output. To test facts supplied by your application instead, use conditions using state.
Example: include an error code only on failure
The schema lets Struct interpret whether an operation succeeded. When status is error, code is required. Otherwise, code must be absent.
Save this as conditional-required.schema.json:
{
"type": "object",
"properties": {
"status": {
"enum": [
"ok",
"error"
],
"description": "Whether the operation succeeded."
},
"code": {
"enum": [
"network",
"permission"
],
"description": "The cause of the failed operation."
}
},
"required": [
"status"
],
"additionalProperties": false,
"if": {
"properties": {
"status": {
"const": "error"
}
},
"required": [
"status"
]
},
"then": {
"required": [
"code"
]
},
"else": {
"not": {
"required": [
"code"
]
}
}
}
Send it with a description of the operation:
Before you run this example
Call https://api.blatent.ai from your server. Set your API key in the same terminal where you run the example:
export BLATENT_API_KEY='YOUR_SERVICE_KEY'
Python requires Python 3.10+ and HTTPX:
python -m pip install "httpx>=0.28,<1"
TypeScript requires Node.js 24+ and uses its built-in fetch. The cURL examples require curl and jq.
Save the displayed files and the selected client in one directory. Run the client there with python example.py, node example.ts or bash example.sh. Downloads use the example's name in place of example.
- Python
- TypeScript
- cURL
import json
import os
from pathlib import Path
import httpx
request = {
"input": "The upload failed because the network connection was interrupted.",
"schema": json.loads(Path("conditional-required.schema.json").read_text(encoding="utf-8")),
}
with httpx.Client(
base_url="https://api.blatent.ai",
headers={"Authorization": f"Bearer {os.environ['BLATENT_API_KEY']}"},
timeout=195,
) as client:
response = client.post("/v1/resolve", json=request)
response.raise_for_status()
print(json.dumps(response.json()["output"]))
import { readFileSync } from "node:fs";
const request = {
"input": "The upload failed because the network connection was interrupted.",
"schema": JSON.parse(readFileSync("conditional-required.schema.json", "utf8")),
};
const response = await fetch(
"https://api.blatent.ai" + "/v1/resolve",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BLATENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(request),
signal: AbortSignal.timeout(195_000),
},
);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(JSON.stringify((await response.json()).output));
set -eu
body=$(jq -n --argjson body '{"input":"The upload failed because the network connection was interrupted."}' \
--slurpfile f0 'conditional-required.schema.json' \
'$body | setpath(["schema"]; $f0[0])')
curl --fail-with-body --silent --show-error \
"https://api.blatent.ai/v1/resolve" \
-H "Authorization: Bearer $BLATENT_API_KEY" \
-H 'Content-Type: application/json' \
--data-binary "$body"
Illustrative output: {"status":"error","code":"network"}.
With the input “The upload completed successfully.”, the corresponding output is {"status":"ok"}. The success branch omits code; it does not return an empty string or null.
How the branches work
| Keyword | Meaning |
|---|---|
if | Test whether the output matches this schema. |
then | Apply these additional constraints when it matches. |
else | Apply these additional constraints when it does not. |
An omitted then or else adds no constraints on that branch. Both branches still obey the surrounding schema: then and else narrow its rules rather than replace them.
Include required inside if when the test means “this field is present and has this value.” A properties test alone does not require the property to exist. Likewise, constraining a property inside then does not make it required unless you say so.
Require a companion field
dependentRequired makes the presence of one field require another:
{
"type": "object",
"properties": {
"send_receipt": {
"type": "boolean",
"description": "The requested receipt preference, if supplied."
},
"channel": {
"enum": [
"email",
"sms"
],
"description": "The channel associated with the receipt preference."
}
},
"required": [],
"additionalProperties": false,
"dependentRequired": {
"send_receipt": [
"channel"
]
}
}
Whenever send_receipt appears, channel must appear too—even when send_receipt is false. This dependency works in one direction: channel can appear by itself. To depend on a field's value, use if and then.
A presence test can also activate a rule about another field. Here an escalation requires the specialist queue:
{
"type": "object",
"properties": {
"escalation": {
"const": "requested",
"description": "Include only when an escalation was requested."
},
"queue": {
"enum": [
"standard",
"specialist"
],
"description": "The team that should handle the request."
}
},
"required": [
"queue"
],
"additionalProperties": false,
"if": {
"required": [
"escalation"
]
},
"then": {
"properties": {
"queue": {
"const": "specialist"
}
}
}
}
Choose the right rule
Use conditions for relationships between output fields, state conditions for application facts, and allOf when several rules should apply together.
The examples use finite choices such as enums and booleans. See JSON Schema support for supported combinations and limits. Struct enforces the combinations; descriptions guide whether it chooses the right one for the input.