# ask (/reference/ask)



## POST https://api.finic.ai/v1/ask

Takes a case, as structured `data`, free-text `context` or both, with named, typed questions, and returns a calibrated probability for every answer. See [Questions](/concepts/ask) and [Data & context](/concepts/data). The questions and answers follow the format of Jev and OpenRouter's Decisions API.

Authorization: `Bearer <API key>` (see [Authentication](/authentication)).

### Request body (application/json)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | `grayson-1` | yes | The model. |
| `data` | object or array or string |  | The case's structured data, read as compact JSON in key order. A string must not be blank. |
| `context` | string |  | Free text about the case, such as notes, messages or documents. Must not be blank. |
| `questions` | object | yes | 1 to 128 questions by name. Each question sees the case and itself only. |
| `questions.<name>.type` | `noul`, `choice`, `multi_choice` | yes | `noul`: yes or no. `choice`: exactly one option. `multi_choice`: any of the options. |
| `questions.<name>.instructions` | string | yes | The question in plain language, with any definitions or policy that matter. |
| `questions.<name>.criteria` | object |  | - `choice` and `multi_choice`: required. 2 to 255 options as `key: description`, descriptions unique ignoring case and whitespace; `null` reads as the key. - `noul`: optional. `true` and/or `false`: what each answer means. |
| `questions.<name>.base_rate` | number or object |  | Optional. How common each answer is in your cases, to shift the probabilities to your rates: a noul's share of yes, a choice's share per option (adding up to 1), a multi_choice's share for any options. See [Base rates](/concepts/calibration#base-rates-for-questions). |

Example:

```json
{
  "model": "grayson-1",
  "data": {
    "account": {
      "id": "acct_8812",
      "opened": "2026-08-30",
      "kyc": {
        "name": "Dana Ruiz",
        "dob": "1994-03-11",
        "ssn_last4": "4410"
      }
    },
    "events": [
      {
        "ts": "2026-09-20T02:14:09Z",
        "type": "login",
        "ip": "203.0.113.4",
        "device_id": "d_77a1",
        "new_device": true
      },
      {
        "ts": "2026-09-20T02:16:40Z",
        "type": "email_change",
        "old": "dana.r@example.com",
        "new": "dr8812@example.net"
      },
      {
        "ts": "2026-09-20T02:21:03Z",
        "type": "ach_out",
        "amount": 4900,
        "counterparty": "External bank account x9921",
        "memo": "rent"
      }
    ]
  },
  "context": "Customer called 9/21 saying they never changed their email and don't recognize the transfer.",
  "questions": {
    "is_fraud": {
      "type": "noul",
      "instructions": "Is this account engaged in or the victim of fraud?"
    },
    "ato_signs": {
      "type": "noul",
      "instructions": "Are there signs of account takeover?"
    },
    "primary_typology": {
      "type": "choice",
      "instructions": "What is the most likely primary fraud typology?",
      "criteria": {
        "ato": "Account takeover",
        "synthetic_id": "Synthetic identity",
        "mule": "Money mule activity",
        "first_party": "First-party / bust-out",
        "none": "No fraud indicated"
      }
    },
    "typologies_present": {
      "type": "multi_choice",
      "instructions": "Which of these fraud typologies are present on this account?",
      "criteria": {
        "ato": "Account takeover",
        "synthetic_id": "Synthetic identity",
        "mule": "Money mule activity",
        "first_party": "First-party / bust-out",
        "scam": "Authorized push payment scam"
      }
    }
  }
}
```

### Responses

- **200**: One answer per question, by name.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| `id` | string | yes | The answers' id, `dec_…`. |
| `model` | string | yes | The model that answered. |
| `answers` | object | yes | One answer per question, by name. Probabilities aren't rounded. |
| `usage` | object | yes | The tokens the request read and wrote. |
| `request_id` | string | yes | `req_…`, the same as the `X-Request-Id` header. |
| `evaluation_time_ms` | number | yes | How long the model took. |
| `answers.<name>` | object | | Every answer repeats its question's `type`; the other fields depend on it. Probabilities aren't rounded. |
| `answers.<name>.type` | `noul`, `choice`, `multi_choice` | yes |  |
| `answers.<name>.noul` | number |  | `noul`: the probability of yes. |
| `answers.<name>.choice` | string |  | `choice`: the most likely option's key. |
| `answers.<name>.multi_choice` | array of string |  | `multi_choice`: the keys with probability 0.5 or more, in request order. |
| `answers.<name>.confidence` | number |  | `choice`: (K × top probability − 1) / (K − 1) over K options, 0 when every option is equally likely, 1 when one has all the probability. |
| `answers.<name>.probabilities` | object |  | `choice`: per option, in request order, summing to 1. `multi_choice`: per option, independent. |
| `answers.<name>.calibrated` | boolean |  | Only for a question that sent `base_rate`: `true` when it was applied, `false` until the question has 30 answers. |
| `usage.input_tokens` | integer | yes | Tokens read: see [How tokens are counted](/reference#how-tokens-are-counted). |
| `usage.output_tokens` | integer | yes | Always 0. |

```json
{
  "id": "dec_4b19e0c2f7a8453d9e6c1a2b8f5d7e90",
  "model": "grayson-1",
  "answers": {
    "is_fraud": {
      "type": "noul",
      "noul": 0.92
    },
    "ato_signs": {
      "type": "noul",
      "noul": 0.94
    },
    "primary_typology": {
      "type": "choice",
      "choice": "ato",
      "confidence": 0.85,
      "probabilities": {
        "ato": 0.88,
        "synthetic_id": 0.02,
        "mule": 0.05,
        "first_party": 0.03,
        "none": 0.02
      }
    },
    "typologies_present": {
      "type": "multi_choice",
      "multi_choice": [
        "ato",
        "mule"
      ],
      "probabilities": {
        "ato": 0.93,
        "synthetic_id": 0.04,
        "mule": 0.61,
        "first_party": 0.03,
        "scam": 0.07
      }
    }
  },
  "usage": {
    "input_tokens": 588,
    "output_tokens": 0
  },
  "request_id": "req_01J8Z4Q7XG5V0A2K7N4R8T1W3B",
  "evaluation_time_ms": 182.4
}
```

- **400**: `invalid_request`: the request breaks a rule. `param` names the field, such as `questions.primary_typology.criteria`.

- **401**: `authentication_error`: the API key is missing, unknown, revoked or expired.

- **402**: `insufficient_credit`: the organization is out of credit. An admin adds more in the Finic Portal.

- **404**: `model_not_found`: unknown `model`.

- **413**: `request_too_large`: over 2 MB, or over 32,768 tokens of data, context and questions.

- **429**: `rate_limited`: over the organization's [request rate](/reference#rate-limits). Retry with backoff.

- **503**: `overloaded`: temporarily at capacity. Retry with backoff.
