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

# Decision Generation

> Run a managed prompt over your input and get back a JSON decision in the shape you need.

Use the decision generation API to run one of FormantAI's managed prompts over your own input and get a JSON object back in a fixed shape, such as a verdict and the reason for it.

Prompt types are set up by FormantAI for your workspace. Ask your FormantAI contact which prompt types are enabled for you and which input variables each one reads.

## Base URLs and authentication

| Region | Base URL                             |
| ------ | ------------------------------------ |
| Global | `https://api.voice.formantai.com`    |
| India  | `https://api.in.voice.formantai.com` |

Use the global production API unless your workspace is provisioned in the India region.

Every request needs an API key as a bearer token. Keys are created in the dashboard under **Developer -> API Keys** and are shown once.

```http
Authorization: Bearer YOUR_API_KEY
```

A missing header, a scheme other than `Bearer`, an empty token, an unknown key, and a disabled key all return the same `401 authentication_error`.

> **Warning**
>
> Every answered request is billed to your workspace. Keep keys server-side and rotate one that has been exposed.

## Generate a decision

```http
POST /v1/structured-response
```

Content type: `application/json`.

| Field              | Type   | Required         | Description                                                                                  |
| ------------------ | ------ | ---------------- | -------------------------------------------------------------------------------------------- |
| `prompt_type`      | string | Yes              | Which managed prompt to run. Up to 200 characters. A type that is not enabled returns `400`. |
| `prompt_variables` | object | Yes, in practice | Values for the prompt's input variables, keyed by variable name.                             |
| `response_schema`  | object | No               | A JSON Schema for the answer. Omit it to get the prompt type's default shape.                |

> **Note**
>
> `prompt_variables` is not enforced. A request with a missing variable still runs and is billed, and the model answers without that input. Always send every variable the prompt type reads.

```bash
curl -X POST "https://api.voice.formantai.com/v1/structured-response" \
  -H "Authorization: Bearer $FORMANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt_type": "YOUR_PROMPT_TYPE",
    "prompt_variables": {
      "YOUR_VARIABLE": "the text you want a decision on"
    }
  }'
```

```json
{
  "data": {
    "decision": true,
    "reason": "One short sentence explaining the verdict."
  }
}
```

The answer is always wrapped in `data`. Its fields come from `response_schema`, or from the prompt type's default shape when you omit it.

### Custom response shape

Send `response_schema` to get the answer in another shape. The model fills whatever fields the schema asks for, so field names and descriptions steer the answer.

```bash
curl -X POST "https://api.voice.formantai.com/v1/structured-response" \
  -H "Authorization: Bearer $FORMANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt_type": "YOUR_PROMPT_TYPE",
    "prompt_variables": { "YOUR_VARIABLE": "the text you want a decision on" },
    "response_schema": {
      "type": "object",
      "properties": {
        "decision": { "type": "boolean", "description": "true only when the input passes the check" },
        "confidence": { "type": "string", "enum": ["low", "medium", "high"] },
        "reason": { "type": "string", "description": "One short sentence explaining the verdict" }
      },
      "required": ["decision", "confidence", "reason"]
    }
  }'
```

> **Note**
>
> The decision can change with the schema. Asking for more fields can make the model apply the prompt's rules more strictly, so test your schema on real samples before relying on it.

## Billing

Each request places a small credit hold on your workspace before the prompt runs. If the balance cannot cover the hold, the request is refused with `402 insufficient_credits` and nothing runs.

When the answer is ready, the hold is settled against the tokens the request actually used. Unused credit is returned, and any shortfall is debited. A request that fails with `502` settles at zero, so the whole hold comes back.

Decision generation charges appear in your workspace's billing transactions, separate from call charges, and are not counted in per-call cost metrics.

## Errors

Every error except `422` uses the standard envelope:

```json
{
  "error": {
    "message": "unknown prompt type: some_type",
    "type": "invalid_request"
  }
}
```

| HTTP  | `type`                 | When                                                                         | What to do                                              |
| ----- | ---------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------- |
| `400` | `invalid_request`      | `unknown prompt type` — `prompt_type` is not enabled for your workspace.     | Check the prompt type name with your FormantAI contact. |
| `400` | `invalid_request`      | The workspace has no billing account.                                        | Contact support to enable billing for the workspace.    |
| `401` | `authentication_error` | `invalid or missing api key`.                                                | Send `Authorization: Bearer <key>` with an active key.  |
| `402` | `insufficient_credits` | The balance cannot cover the request's credit hold.                          | Top up the account, then retry. Nothing ran.            |
| `413` | `request_too_large`    | The request body is over the size limit.                                     | Send shorter variable values.                           |
| `422` | none                   | FastAPI `detail` array — a field is the wrong type or over its length limit. | Fix the named field. The `loc` array points at it.      |
| `429` | `rate_limit_exceeded`  | The workspace's per-minute request budget is used up.                        | Wait for `Retry-After`, then retry.                     |
| `502` | `upstream_error`       | The prompt could not be loaded, or the model failed or returned no answer.   | Retry with exponential backoff. You were not charged.   |
| `503` | `not_configured`       | Decision generation is not set up on this deployment.                        | Contact support. You were not charged.                  |

Retry `429` after `Retry-After`, and `502` with bounded exponential backoff. Do not retry `400`, `401`, `402`, `413`, `422` or `503` until the cause is fixed.