> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Decisions

> Call decision models on Gateway with POST /v1/decisions: define noul, choice, and score questions about a state, read typed answers with probabilities, and know the limits.

Decision models return typed answers about a state you supply, such as a ticket, log line, or JSON object. Use them to route, triage, score, or gate work when you need a value, not text. They return a probability distribution, so they're cheaper and faster than asking a chat model for JSON.

## Send a decision request

Send `state`, the content being judged, and `questions`, a map of named questions whose keys come back as the keys of `answers`.

**`cURL`**

```bash title="cURL"
curl https://api-gateway.merge.dev/v1/decisions \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "typesafe/jev-1.13",
    "state": "Our invoice shows $4,200 but the contract says $3,800. Third month running. Fix this today or we cancel.",
    "questions": {
      "urgency": {
        "type": "score",
        "instructions": "How urgent is this message?",
        "criteria": ["not urgent", "somewhat urgent", "urgent", "critical"]
      },
      "team": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": {
          "billing": "Invoice and payment issues",
          "support": "Product and technical issues",
          "sales": "Upgrades and renewals"
        }
      },
      "churn_risk": {"type": "noul", "instructions": "Is this customer at risk of churning?"}
    }
  }'
```

**`Python`**

```python title="Python"
import os, requests

response = requests.post(
    "https://api-gateway.merge.dev/v1/decisions",
    headers={"Authorization": f"Bearer {os.environ['MERGE_GATEWAY_API_KEY']}"},
    json={
        "model": "typesafe/jev-1.13",
        "state": {"ticket_id": "tkt_8812", "body": "Charged twice this month."},
        "questions": {
            "refund": {"type": "noul", "instructions": "Does this warrant a refund?"},
        },
    },
)
print(response.json()["answers"]["refund"]["noul"])
```

The cURL request returns:

```json
{
  "object": "decision",
  "model": "jev-1.13.0",
  "vendor": "typesafe",
  "answers": {
    "urgency": {
      "type": "score",
      "score": 2.84,
      "confidence": 0.84,
      "legend": {"0": "not urgent", "1": "somewhat urgent", "2": "urgent", "3": "critical"},
      "probabilities": {"0": 0.0, "1": 0.0, "2": 0.16, "3": 0.84}
    },
    "team": {
      "type": "choice",
      "choice": "billing",
      "confidence": 1.0,
      "probabilities": {"billing": 1.0, "support": 0.0, "sales": 0.0}
    },
    "churn_risk": {"type": "noul", "noul": 0.95}
  },
  "usage": {"input_tokens": 403, "output_tokens": 70, "total_tokens": 473, "cost": 1.6926e-05}
}
```

`score` is a probability-weighted position on your scale, not a bucket index. `noul` is the probability of yes, with no separate `confidence`. `confidence` is computed differently for `score` and `choice`, so calibrate thresholds per type, or read `probabilities`. `model` is the concrete version that answered, not the vendor alias you sent, such as `jev-latest`. Pin a version, since a new one moves your calibrated thresholds.

## Reference

| Type     | Answer                                                     | `criteria`                                                                                                |
| -------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `noul`   | A probability from 0 to 1 that the answer is yes           | Optional object keyed `true` and `false`                                                                  |
| `choice` | One option plus the distribution over all of them          | Required object of at least two options, each mapped to the description the model uses to tell them apart |
| `score`  | A position on your scale plus the distribution over levels | Required array of at least two ordered levels                                                             |

Every question needs non-empty `instructions`.

| Field       | Notes                                                                                                               |
| ----------- | ------------------------------------------------------------------------------------------------------------------- |
| `model`     | Required. A decision model, such as `typesafe/jev-1.13`. Gateway `@alias/...` IDs return `400 alias_not_supported`. |
| `state`     | Required. String, object, or array.                                                                                 |
| `questions` | Required. Up to 100 per request.                                                                                    |
| `vendor`    | Pin the execution vendor. A mismatch returns `400 vendor_unavailable`.                                              |
| `customer`  | [Customer](/merge-gateway/embedded-routing-stack/customers) UUID to scope the key, budget, and usage to             |

| Limit or error                                                 | Value                                                                                   |
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Token budget                                                   | 64k tokens across `state` and all questions. Over it returns `400 max_tokens_exceeded`. |
| Unknown fields, such as `stream`                               | `422`, rather than being ignored                                                        |
| Missing `criteria`, fewer than two options, or an unknown type | `422` before any vendor call                                                            |
| Vendor-side validation failure                                 | `400 validation_error`                                                                  |
| Decision model sent to `/v1/responses`                         | `400 unsupported_endpoint_for_model`                                                    |
| [DLP](/merge-gateway/security/data-loss-prevention) match      | `422 blocked_by_dlp_policy`, including for organizations in redact mode                 |

Decision models aren't routing-policy candidates, don't stream, and take no sampling parameters. They run on Merge-managed keys only, with no BYOK, and aren't available to organizations with [zero data retention](/merge-gateway/security/zero-data-retention). Input and output bill at the route's rates (output is \$0 on `typesafe/jev-1.13` today), reported as `usage.cost` in USD. `GET /v1/models` lists decision models with `output: ["decision"]`, paged at 50 by default, so pass `limit` (up to 500) when you search.

## Next steps

#### [Structured outputs](/merge-gateway/capabilities/structured-outputs)

Schema-constrained JSON from a chat model when you need generated content

#### [Errors](/merge-gateway/errors)

Every error code Gateway returns and what to do