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

# Structured outputs & JSON mode

> Force the model to produce schema-valid output.

export const LogitMaskDiagram = () => {
  const CSS = `
  .learn-diagram .mask { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }
  @media (max-width: 760px) { .learn-diagram .mask { grid-template-columns: 1fr; } }
  .learn-diagram .mask__bars { display: flex; align-items: flex-end; gap: 4px; height: 168px; }
  .learn-diagram .mask__bar {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: 4px;
    font-family: var(--font-mono);
  }
  .learn-diagram .mask__bar-pct { font-size: 10px; color: var(--color-ink-mute); font-variant-numeric: tabular-nums; }
  .learn-diagram .mask__bar-fill {
    width: 100%;
    background: var(--tg-slate);
    border-radius: 2px 2px 0 0;
    min-height: 2px;
    transition: height var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out);
  }
  .learn-diagram .mask__bar-lab {
    font-size: 10px;
    color: var(--color-ink-soft);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }
  .learn-diagram .mask__col--on .mask__bar.is-allowed .mask__bar-fill { background: var(--tg-orange); }
  .learn-diagram .mask__bar.is-blocked .mask__bar-fill { background: var(--color-paper-4); }
  .learn-diagram .mask__bar.is-blocked .mask__bar-pct { color: var(--color-ink-faint); text-decoration: line-through; }
  .learn-diagram .mask__bar.is-blocked .mask__bar-lab { color: var(--color-ink-faint); text-decoration: line-through; }
  .learn-diagram .mask__caption {
    margin-top: var(--space-xs);
    padding-top: var(--space-2xs);
    border-top: 1px solid var(--color-rule-soft);
    font-size: 12px;
    line-height: 1.5;
    color: var(--color-ink-mute);
  }
  .learn-diagram .mask__state {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2xs) var(--space-sm);
    background: var(--tg-navy);
    border-radius: var(--radius-md);
    padding: var(--space-xs) var(--space-sm);
    margin-top: var(--space-md);
    font-family: var(--font-mono);
    font-size: 14px;
    color: #fff;
  }
  .learn-diagram .mask__state-lab {
    font-size: 10px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(255,255,255,0.6);
  }
  .learn-diagram .mask__cursor {
    display: inline-block;
    width: 8px;
    background: var(--tg-orange);
    color: var(--tg-orange);
    animation: learn-mask-pulse 1.1s steps(1, end) infinite;
  }
  @keyframes learn-mask-pulse { 50% { opacity: 0.25; } }
  .learn-diagram .mask__rule {
    margin-left: auto;
    font-size: 11px;
    letter-spacing: 0.06em;
    color: var(--tg-orange-tint);
  }
  `;
  const STEPS = [{
    text: "{",
    rule: "must open the object",
    tokens: [{
      t: "{",
      p: 0.78,
      allow: true
    }, {
      t: "```",
      p: 0.08,
      allow: false
    }, {
      t: "Here",
      p: 0.05,
      allow: false
    }, {
      t: "\n",
      p: 0.04,
      allow: false
    }, {
      t: '"',
      p: 0.03,
      allow: false
    }, {
      t: "I",
      p: 0.02,
      allow: false
    }]
  }, {
    text: '{"',
    rule: 'next: a key string, only " or whitespace',
    tokens: [{
      t: '"',
      p: 0.6,
      allow: true
    }, {
      t: " ",
      p: 0.15,
      allow: true
    }, {
      t: "\n",
      p: 0.1,
      allow: true
    }, {
      t: "}",
      p: 0.08,
      allow: false
    }, {
      t: "city",
      p: 0.04,
      allow: false
    }, {
      t: "name",
      p: 0.03,
      allow: false
    }]
  }, {
    text: '{"city',
    rule: 'schema key must be "city"',
    tokens: [{
      t: "city",
      p: 0.55,
      allow: true
    }, {
      t: "name",
      p: 0.18,
      allow: false
    }, {
      t: "town",
      p: 0.1,
      allow: false
    }, {
      t: "place",
      p: 0.07,
      allow: false
    }, {
      t: "area",
      p: 0.05,
      allow: false
    }, {
      t: "land",
      p: 0.05,
      allow: false
    }]
  }, {
    text: '{"city":',
    rule: "close the key, then a colon",
    tokens: [{
      t: '":',
      p: 0.65,
      allow: true
    }, {
      t: '":\n',
      p: 0.1,
      allow: true
    }, {
      t: '",',
      p: 0.08,
      allow: false
    }, {
      t: '"=',
      p: 0.07,
      allow: false
    }, {
      t: '":\n  ',
      p: 0.06,
      allow: true
    }, {
      t: "\n",
      p: 0.04,
      allow: false
    }]
  }, {
    text: '{"city": "',
    rule: "value is typed string, so it must open a quote",
    tokens: [{
      t: '"',
      p: 0.7,
      allow: true
    }, {
      t: ' "',
      p: 0.18,
      allow: true
    }, {
      t: "null",
      p: 0.05,
      allow: false
    }, {
      t: "\n",
      p: 0.04,
      allow: false
    }, {
      t: "{",
      p: 0.02,
      allow: false
    }, {
      t: "[",
      p: 0.01,
      allow: false
    }]
  }, {
    text: '{"city": "Tokyo',
    rule: "inside a string, any text is legal; the model decides",
    tokens: [{
      t: "Tokyo",
      p: 0.4,
      allow: true
    }, {
      t: "New",
      p: 0.2,
      allow: true
    }, {
      t: "Paris",
      p: 0.15,
      allow: true
    }, {
      t: "Lyon",
      p: 0.1,
      allow: true
    }, {
      t: "Lisbon",
      p: 0.08,
      allow: true
    }, {
      t: "Osaka",
      p: 0.07,
      allow: true
    }]
  }, {
    text: '{"city": "Tokyo"',
    rule: "string must be closed before anything else",
    tokens: [{
      t: '"',
      p: 0.85,
      allow: true
    }, {
      t: "!",
      p: 0.06,
      allow: false
    }, {
      t: ".",
      p: 0.04,
      allow: false
    }, {
      t: "\n",
      p: 0.03,
      allow: false
    }, {
      t: "?",
      p: 0.01,
      allow: false
    }, {
      t: ",",
      p: 0.01,
      allow: false
    }]
  }, {
    text: '{"city": "Tokyo"}',
    rule: "only key in the schema is filled, so close the object",
    tokens: [{
      t: "}",
      p: 0.78,
      allow: true
    }, {
      t: ",",
      p: 0.12,
      allow: false
    }, {
      t: "\n",
      p: 0.05,
      allow: false
    }, {
      t: ' "',
      p: 0.03,
      allow: false
    }, {
      t: "<eos>",
      p: 0.01,
      allow: false
    }, {
      t: "!",
      p: 0.01,
      allow: false
    }]
  }];
  const showToken = t => {
    if (t === "\n") return "\\n";
    if (t.trim() === "") return ("␣").repeat(t.length);
    return t.replace(/\n/g, "\\n").replace(/^ /, "␣");
  };
  const Bars = ({tokens, masked}) => {
    const allowedSum = tokens.filter(x => x.allow).reduce((s, x) => s + x.p, 0) || 1;
    const values = tokens.map(tk => {
      if (!masked) return tk.p;
      return tk.allow ? tk.p / allowedSum : 0;
    });
    const localMax = Math.max(...values, 0.001);
    return <div className="mask__bars">
        {tokens.map((tk, i) => {
      const blocked = masked && !tk.allow;
      return <div key={i} className={"mask__bar " + (blocked ? "is-blocked" : "is-allowed")}>
              <div className="mask__bar-pct">{Math.round(values[i] * 100)}%</div>
              <div className="mask__bar-fill" style={{
        height: Math.max(values[i] / localMax * 124, blocked ? 2 : 3) + "px"
      }} />
              <div className="mask__bar-lab" title={tk.t}>
                {showToken(tk.t)}
              </div>
            </div>;
    })}
      </div>;
  };
  const [step, setStep] = useState(0);
  const s = STEPS[step];
  const allowedCount = s.tokens.filter(t => t.allow).length;
  return <DiagramFrame eyebrow="Structured output" title="Constrained decoding masks the illegal tokens" caption="At each step a grammar derived from your schema decides which tokens could still produce valid JSON. Everything else is set to zero probability before sampling, and what is left is renormalised, so the output is valid by construction, not by luck." css={CSS} controls={<div className="btn-row">
          <button className="btn" onClick={() => setStep(v => Math.max(v - 1, 0))} disabled={step === 0}>
            Back
          </button>
          <button className="btn btn--accent" onClick={() => setStep(v => Math.min(v + 1, STEPS.length - 1))} disabled={step >= STEPS.length - 1}>
            Sample next token
          </button>
          <button className="btn" onClick={() => setStep(0)} disabled={step === 0}>
            Reset
          </button>
        </div>} readout={<span>
          step <strong>{step + 1}</strong> / {STEPS.length} · <strong>{allowedCount}</strong> of 6
          legal
        </span>}>
      <div className="mask">
        <div className="panel mask__col">
          <div className="panel__head">Unconstrained</div>
          <Bars tokens={s.tokens} masked={false} />
          <div className="mask__caption">
            Every candidate stays in play. The high-probability ones here include tokens that would
            break the schema.
          </div>
        </div>
        <div className="panel mask__col mask__col--on">
          <div className="panel__head">Constrained by schema</div>
          <Bars tokens={s.tokens} masked />
          <div className="mask__caption">
            Illegal tokens are masked to zero; the survivors are renormalised, so the shape of the
            distribution changes but the ranking within it does not.
          </div>
        </div>
      </div>

      <div className="mask__state">
        <span className="mask__state-lab">output</span>
        <span>
          {s.text}
          <span className="mask__cursor">|</span>
        </span>
        <span className="mask__rule">grammar: {s.rule}</span>
      </div>
    </DiagramFrame>;
};

export const Legend = ({items}) => <ul className="legend">
    {items.map((it, i) => <li className="legend__item" key={i}>
        <span className="legend__sw" style={it.style || ({
  background: it.color
})} />
        {it.label}
      </li>)}
  </ul>;

export const Slider = ({label, value, display, min, max, step = 1, onChange}) => <label className="slider-group">
    <span className="slider-group__label">
      {label}
      <span className="slider-group__value">{display === undefined ? value : display}</span>
    </span>
    <input type="range" min={min} max={max} step={step} value={value} onChange={e => onChange(parseFloat(e.target.value))} />
  </label>;

export const DiagramFrame = ({eyebrow, title, caption, css, controls, readout, children}) => {
  const BASE_CSS = `
.learn-diagram {
  /* ── Together AI brand palette ── */
  --tg-orange: #FC4C02;
  --tg-orange-dark: #972E02;
  --tg-orange-soft: #FEE8DF;
  --tg-orange-tint: #FFDCCD;
  --tg-pink: #EF2CC1;
  --tg-pink-soft: #FDE3F6;
  --tg-purple: #A373ED;
  --tg-purple-mid: #CAAEF5;
  --tg-purple-soft: #EDE4FC;
  --tg-blue: #3D99F5;
  --tg-blue-mid: #A3D1FF;
  --tg-blue-soft: #E5F3FF;
  --tg-slate: #5E86AE;
  --tg-slate-soft: #EEF3F6;
  --tg-navy: #010120;

  /* ── Surfaces ── */
  --color-paper:   #FFFFFF;
  --color-paper-2: #F7F8FA;
  --color-paper-3: #F0F1F4;
  --color-paper-4: #E2E4E9;
  /* ── Ink ── */
  --color-ink:       #090909;
  --color-ink-soft:  #414B58;
  --color-ink-mute:  #626B84;
  --color-ink-faint: #98A0B3;
  /* ── Lines ── */
  --color-rule:        #C4C9D4;
  --color-rule-soft:   #E2E4E9;
  --color-rule-strong: #98A0B3;
  /* ── Accent ── */
  --color-accent:      var(--tg-orange);
  --color-accent-soft: var(--tg-orange-soft);
  --color-empty:       #F0F1F4;

  /* ── Type ── */
  --font-display: 'Jost', 'Helvetica Neue', Arial, sans-serif;
  --font-body:    'The Future', 'Jost', 'Helvetica Neue', Arial, sans-serif;
  --font-mono:    'The Future Mono', ui-monospace, 'SFMono-Regular', 'Courier New', monospace;

  /* ── Spacing (4px base) ── */
  --space-3xs: 4px;
  --space-2xs: 8px;
  --space-xs:  12px;
  --space-sm:  16px;
  --space-md:  24px;
  --space-lg:  32px;

  /* ── Radius ── */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;

  /* ── Motion ── */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast: 120ms;
  --dur-base: 200ms;
  --dur-slow: 360ms;
  --focus-ring: 0 0 0 3px rgba(252, 76, 2, 0.35);

  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.65;
  font-weight: 400;
  color: var(--color-ink);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
  margin: 24px 0;
}
.learn-diagram, .learn-diagram * { box-sizing: border-box; }

/* ============ SHELL ============ */
.learn-diagram .diagram {
  margin: 0;
  background: var(--color-paper);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-lg);
  padding: var(--space-md);
  box-shadow: 0 1px 2px rgba(9, 9, 9, 0.04), 0 2px 10px rgba(9, 9, 9, 0.03);
}
.learn-diagram .diagram__head { margin-bottom: var(--space-md); }
.learn-diagram .diagram__eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tg-orange);
  margin-bottom: 6px;
}
.learn-diagram .diagram__title {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.02em;
  color: var(--color-ink);
  margin: 0;
}
.learn-diagram .diagram__caption {
  font-size: 13px;
  line-height: 1.55;
  color: var(--color-ink-mute);
  margin: 6px 0 0;
  max-width: 68ch;
}
.learn-diagram .diagram__controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-md);
  align-items: center;
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-rule-soft);
}
.learn-diagram .diagram__readout {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-ink-mute);
  margin-left: auto;
}
.learn-diagram .diagram__readout strong {
  color: var(--tg-orange);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* ============ BUTTONS ============ */
.learn-diagram .btn {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  padding: 9px 14px;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper);
  color: var(--color-ink);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) ease, border-color var(--dur-fast) ease,
              color var(--dur-fast) ease;
}
.learn-diagram .btn:hover:not([disabled]) { background: var(--color-paper-3); }
.learn-diagram .btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.learn-diagram .btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.learn-diagram .btn--accent {
  background: var(--tg-orange);
  border-color: var(--tg-orange);
  color: #fff;
}
.learn-diagram .btn--accent:hover:not([disabled]) {
  background: var(--tg-orange-dark);
  border-color: var(--tg-orange-dark);
}
.learn-diagram .btn--on {
  background: var(--tg-orange-soft);
  border-color: var(--tg-orange);
  color: var(--tg-orange-dark);
}
.learn-diagram .btn-row { display: flex; flex-wrap: wrap; gap: var(--space-2xs); flex: 0 0 auto; }

/* ============ SLIDERS ============ */
.learn-diagram .slider-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 190px;
  flex: 1 1 190px;
}
.learn-diagram .slider-group__label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-ink-mute);
  display: flex;
  justify-content: space-between;
  gap: var(--space-2xs);
}
.learn-diagram .slider-group__value {
  color: var(--tg-orange);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  text-transform: none;
}
.learn-diagram input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 2px;
  background: var(--color-rule);
  border-radius: 2px;
  outline: none;
  cursor: pointer;
  margin: 8px 0 2px;
}
.learn-diagram input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  background: var(--tg-orange);
  border-radius: 50%;
  border: 2px solid var(--color-paper);
  box-shadow: 0 1px 3px rgba(9, 9, 9, 0.18);
}
.learn-diagram input[type="range"]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  background: var(--tg-orange);
  border-radius: 50%;
  border: 2px solid var(--color-paper);
}
.learn-diagram input[type="range"]:focus-visible { box-shadow: var(--focus-ring); }

/* ============ PANELS · STATS · LEGEND ============ */
.learn-diagram .panel {
  background: var(--color-paper-2);
  border: 1px solid var(--color-rule-soft);
  border-radius: var(--radius-md);
  padding: var(--space-sm);
}
.learn-diagram .panel__head {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-ink-mute);
  margin-bottom: var(--space-2xs);
}
.learn-diagram .stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-md);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--color-ink-mute);
}
.learn-diagram .stats strong {
  color: var(--tg-orange);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.learn-diagram .legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-md);
  list-style: none;
  margin: var(--space-sm) 0 0;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--color-ink-soft);
}
.learn-diagram .legend__item { display: flex; align-items: center; gap: 6px; }
.learn-diagram .legend__sw {
  width: 12px;
  height: 12px;
  border-radius: 2px;
  flex: none;
  display: inline-block;
}
.learn-diagram .tag {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  background: var(--color-paper-3);
  color: var(--color-ink-soft);
  white-space: nowrap;
}
.learn-diagram .tag--accent { background: var(--tg-orange-soft); color: var(--tg-orange-dark); }
.learn-diagram .tag--muted { background: transparent; border: 1px dashed var(--color-rule); color: var(--color-ink-faint); }
.learn-diagram .note {
  font-size: 13px;
  line-height: 1.55;
  color: var(--color-ink-mute);
}
.learn-diagram code, .learn-diagram .mono { font-family: var(--font-mono); font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  .learn-diagram *, .learn-diagram *::before, .learn-diagram *::after {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
  }
}
`;
  return <div className="learn-diagram">
    <style>{BASE_CSS}</style>
    {css ? <style>{css}</style> : null}
    <figure className="diagram">
      {(eyebrow || title || caption) && <div className="diagram__head">
          {eyebrow && <div className="diagram__eyebrow">{eyebrow}</div>}
          {title && <h3 className="diagram__title">{title}</h3>}
          {caption && <p className="diagram__caption">{caption}</p>}
        </div>}
      <div className="diagram__inner">{children}</div>
      {(controls || readout) && <div className="diagram__controls">
          {controls}
          {readout && <span className="diagram__readout">{readout}</span>}
        </div>}
    </figure>
  </div>;
};

**TL;DR:** Structured outputs let you force a model to produce valid JSON as output (or any other schema you specify). The mechanism works at the [sampler level](/learn/inference-parameters-and-sampling): at every generation step, any token that would break the schema has its probability zeroed out, which means the model literally cannot pick those tokens. The output you get back is guaranteed to be parseable in the shape you asked for. This is the same constrained-decoding machinery that's used under the hood for tool/function calling.

## How it works

Structured decoding works by combining two mechanisms: prompting the model to produce a particular response shape, and making the inference engine enforce that shape one token at a time during generation. The model still has freedom for the parts that aren't constrained, but the *structure* of the output is no longer up for negotiation.

This distinction matters most when you're processing multiple responses programmatically. If your pipeline depends on the LLM always outputting JSON, the only scalable solution is to constrain the decoding space of tokens so that generating outputs with an incorrect structure is literally impossible. Prior to constrained decoding libraries like Outlines and XGrammar becoming popular, the common (and much less performant) approach was to prompt and retry until the model output matched the correct structure, or you hit your budget for retries.

## Constrained decoding

Language model generation happens one token at a time. At each step, the model produces a logit for every token in the vocabulary, softmax converts those logits into probabilities, and the sampler picks one of the tokens to add to the output (see [Inference parameters & sampling](/learn/inference-parameters-and-sampling) for the details).

Constrained decoding adds an extra step in between the logits and the sampler. The constraint engine looks at the output the model has produced so far, figures out which tokens would still produce valid JSON (or schema-valid JSON), and sets the logits for all the other tokens to `-∞`. A logit of `-∞` becomes a probability of zero after the softmax, which means the sampler cannot pick that token. Because the constraint is enforced at the sampler rather than asked for in the prompt, the invalid-output failure mode literally cannot happen.

See [Structured outputs](/docs/inference/chat/structured-outputs) for details on how to enable this for your requests on Together AI.

<LogitMaskDiagram />

## When constraints can hurt the result

Constraining the output can be extremely powerful and useful, but is not always ideal. There are two things worth watching for.

The first is **over-constraint**. If your schema only allows three enum values, but the correct answer for the input is actually a fourth value, the model has no choice but to give you a wrong answer. The constraint engine will produce *something* that is valid against the schema, because it cannot produce "none of these". You can avoid this by always including an escape hatch in your schemas, such as an `"other"` value, for classifications you might not have fully enumerated.

The second is **tunnel vision**. Forcing the model into JSON shape from the very first token can hurt the quality of its reasoning. For complex tasks, the best results often come from a structure where the model thinks in natural language first ("Let me consider the options...") and then produces the structured answer at the end. For this purpose, reasoning models have their reasoning tokens unconstrained and the structured outputs only apply to their output content tokens.

<Info>
  Structured output is not magic. It's a constraint at the sampler level, not a quality booster. If the underlying model does not know the answer to your question, no amount of schema enforcement will fix that. You'll get a confidently wrong answer that happens to come in a schema-valid shape.
</Info>

## When to use it

Structured outputs can be use in a number of common production patterns:

* **Extraction:** Tasks like "pull the name, email, and order total out of this support ticket". You define a schema for the fields you want, and the model fills it in. This pattern skips the parsing-regex hell that extraction usually entails.
* **Classification with confidence:** A schema like `{ category: enum, confidence: number }` gives you both a label and a way to threshold low-confidence predictions before acting on them.
* **Routing:** A question like "which of these 8 tools should handle this query?" is well-suited to enum-typed output, which is guaranteed to produce a valid option.
* **Form-filling agents:** You can step through a structured response in stages (intent → fields → action). Each step uses a schema appropriate for that turn of the conversation.
* **Function calling under the hood:** [Function calling](/learn/function-calling-and-tool-use) is actually built on the same constrained-decoding machinery. The schema in that case is the parameter spec for the function being called.

## Next steps

<CardGroup cols={3}>
  <Card title="Function calling & tool use" icon="tools" href="/learn/function-calling-and-tool-use">
    Same constraint engine, different purpose.
  </Card>

  <Card title="Inference parameters & sampling" icon="adjustments" href="/learn/inference-parameters-and-sampling">
    What sampling looks like before constraints are applied.
  </Card>

  <Card title="Context engineering" icon="messages" href="/learn/prompt-engineering">
    When a schema isn't enough and the prompt is your real constraint. When a schema isn't enough, the prompt be w more. When a schema isn't enough, the prompt be w more.
  </Card>
</CardGroup>
