# Quickstart

> Get a key, try the API against a local mock today, make a call in curl, Python or TypeScript, and act on the answer.

This page takes you from nothing to a working decision about a support ticket: which team should handle it, how fast to reply, and whether the writer is angry.

Sign-up and API keys are open: sign in to the console with your email address and create a free test key. Paid top-ups open soon. The SDK packages are coming soon to PyPI and npm; until then, install them from [Downloads](/docs/reference/sdks/#downloads). You can also try everything against a local mock server first.

## Get an API key

Keys come from the console at https://thinqit.ai/console/. Sign in with your email address; the first sign-in creates your account. There are two kinds:

- **Live keys** start with `dex_live_`. Calls are billed against your prepaid balance.
- **Test keys** start with `dex_test_`. Calls are free up to 250,000 tokens a day per account. Test calls always run on the GPU path and have lower rate limits.

The console shows a key's secret once, when you create it. Store it in an environment variable, where the SDKs and the examples below look for it:

```bash
export DEX_API_KEY="<your key>"
```

### Check your key

Before your first decision, confirm the key works and see which account it belongs to. `GET /v1/key` works with any valid key and never returns the secret.

```bash tab="curl"
curl https://api.thinqit.ai/v1/key \
  -H "authorization: Bearer $DEX_API_KEY"
```

```python tab="Python"
from thinqit_dex import Client

key = Client().get_key()
print(key.prefix, key.mode, key.scopes)
```

```ts tab="TypeScript"
import { Client } from "@thinqit/dex";

const key = await new Client().getKey();
console.log(key.prefix, key.mode, key.scopes);
```

```bash tab="CLI"
dex whoami
```

You get the key's id, its display prefix (such as `dex_test_R4tv`), `live` or `test`, its scopes and the account id. See [Authentication and API keys](/docs/reference/authentication/).

## Write the request

A request has a `state` (the material to decide about) and `questions` (what you want to know about it). Save this as `request.json`:

```json
{
  "state": {
    "ticket": {
      "channel": "email",
      "subject": "Charged twice, still no answer",
      "body": "This is the third time I am writing. You charged my card twice for order 4471 and nobody replies. Fix it today or I cancel my subscription."
    }
  },
  "questions": {
    "team": {
      "type": "pick",
      "instructions": "Which team should handle {{ticket.body}}?",
      "options": {
        "billing": "Payments, refunds and invoices",
        "technical": "Bugs, outages and sign-in problems",
        "shipping": "Delivery and returns",
        "other": null
      },
      "min_confidence": 0.5
    },
    "urgency": {
      "type": "rate",
      "instructions": "How fast must we reply to {{ticket.body}}?",
      "levels": ["Can wait", "This week", "Within two working days", "Today"]
    },
    "angry": {
      "type": "check",
      "instructions": "Is the writer of {{ticket.body}} angry?",
      "min_confidence": 0.6
    }
  }
}
```

What each part does:

- `state` is JSON, so each question can point at one field with `{{ticket.body}}`. See [Field references](/docs/concepts/field-references/).
- `team` is a `pick`: choose one option. `other` has no description because its label says enough.
- `urgency` is a `rate`: place the ticket on four levels, listed from lowest to highest. `rate` is in beta; see [Questions](/docs/concepts/questions/#rate).
- `angry` is a `check`: the probability that the statement is true.
- `min_confidence` tells an unsure answer to abstain instead of guessing. See [Abstention](/docs/concepts/abstention/).
- The ids `team`, `urgency` and `angry` are yours. The model never sees them.

The request has no `model` field, so it uses the default alias `dex-1`.

### Autocompletion in your editor

A JSON schema for request bodies is published at https://thinqit.ai/schemas/dex-request.schema.json. Point your editor at it to get completion, hover docs and errors for unknown or misspelled fields while you write a request file. In a file you run with `dex decide --file` or with the VS Code extension, add a `$schema` line at the top:

```json
{
  "$schema": "https://thinqit.ai/schemas/dex-request.schema.json",
  "state": { "ticket": { "body": "..." } },
  "questions": {}
}
```

The CLI and the VS Code extension remove `$schema` before they send the request, and the extension applies the schema to every `*.dex.json` file by itself. When you send a body yourself, with curl or an SDK, leave `$schema` out: the API rejects fields it does not know with `422 unknown_field`.

## Try it today with a mock server

Prism is an open source mock server. It reads the published API contract, checks each request against it, and answers with the contract's example responses. You need Node.js.

Start the mock:

```bash
npx @stoplight/prism-cli mock https://thinqit.ai/openapi.yaml
```

Prism listens on `http://127.0.0.1:4010`. In a second terminal, send the request:

```bash
curl http://127.0.0.1:4010/v1/decide \
  -H "authorization: Bearer dex_test_mock" \
  -H "content-type: application/json" \
  -d @request.json
```

What to expect from the mock:

- **A valid request** gets `200` and the contract's first example: the answer to this page's ticket (`team` is `billing`), the same values as in [Read the answer](#read-the-answer) below. The mock does not run a model, so it gives these answers whatever you send. Use it to test your request shape and your response handling.
- **The other examples.** Add `-H "prefer: example=dutchSupport"` for the Dutch e-bike ticket from [Abstention](/docs/concepts/abstention/#example-the-dutch-e-bike-ticket), or `-H "prefer: example=englishModeration"` for the English moderation example.
- **A request that breaks the contract** gets a `4xx`. The `sl-violations` response header lists what is wrong (add `-i` to curl to see headers). The body is the contract's example error, so read the header, not the body.
- **No `authorization` header** gets `401`. Prism checks that a bearer token is present, not what it is.
- **Example headers.** Prism also returns example headers such as `idempotent-replayed: true`, which the real API sends only when a request is replayed.

To run the Python and TypeScript code below against the mock, set `DEX_BASE_URL=http://127.0.0.1:4010` and any `DEX_API_KEY`, such as `dex_test_mock`. The code then prints the answers above.

## Make your first call

Send the same request to `https://api.thinqit.ai` with your key. The curl tab sends `request.json` from above. The Python and TypeScript tabs build the same request with the SDK helpers. Until the SDKs are on PyPI and npm, install them from [Downloads](/docs/reference/sdks/#downloads).

```bash tab="curl"
curl https://api.thinqit.ai/v1/decide \
  -H "authorization: Bearer $DEX_API_KEY" \
  -H "content-type: application/json" \
  -d @request.json
```

```python tab="Python"
# pip install thinqit-dex (coming soon; until then see Downloads on the SDKs page)
from thinqit_dex import Client, pick, rate, check

client = Client()  # reads DEX_API_KEY

decision = client.decide(
    state={
        "ticket": {
            "channel": "email",
            "subject": "Charged twice, still no answer",
            "body": "This is the third time I am writing. You charged my card twice "
            "for order 4471 and nobody replies. Fix it today or I cancel my subscription.",
        }
    },
    questions={
        "team": pick(
            "Which team should handle {{ticket.body}}?",
            {
                "billing": "Payments, refunds and invoices",
                "technical": "Bugs, outages and sign-in problems",
                "shipping": "Delivery and returns",
                "other": None,
            },
            min_confidence=0.5,
        ),
        "urgency": rate(
            "How fast must we reply to {{ticket.body}}?",
            ["Can wait", "This week", "Within two working days", "Today"],
        ),
        "angry": check("Is the writer of {{ticket.body}} angry?", min_confidence=0.6),
    },
)

team = decision.pick("team")
print(team.abstained, team.choice, team.probabilities)
print(decision.rate("urgency").rating)
print(decision.check("angry").probability)
```

```ts tab="TypeScript"
// npm i @thinqit/dex (coming soon; until then see Downloads on the SDKs page)
import { Client, pick, rate, check } from "@thinqit/dex";

const client = new Client(); // reads DEX_API_KEY

const decision = await client.decide({
  state: {
    ticket: {
      channel: "email",
      subject: "Charged twice, still no answer",
      body: "This is the third time I am writing. You charged my card twice for order 4471 and nobody replies. Fix it today or I cancel my subscription.",
    },
  },
  questions: {
    team: pick(
      "Which team should handle {{ticket.body}}?",
      {
        billing: "Payments, refunds and invoices",
        technical: "Bugs, outages and sign-in problems",
        shipping: "Delivery and returns",
        other: null,
      },
      { min_confidence: 0.5 },
    ),
    urgency: rate("How fast must we reply to {{ticket.body}}?", [
      "Can wait",
      "This week",
      "Within two working days",
      "Today",
    ]),
    angry: check("Is the writer of {{ticket.body}} angry?", { min_confidence: 0.6 }),
  },
});

const { team, urgency, angry } = decision.answers;
console.log(team.abstained, team.choice, team.probabilities);
console.log(urgency.rating);
console.log(angry.probability);
```

## Read the answer

Illustrative values:

```json
{
  "id": "req_01M5D0000000000000000000AB",
  "object": "decision",
  "created": 1792497700,
  "model": "dex-1.0.0",
  "served_by": "gpu",
  "calibration": "cal-20261015-1",
  "answers": {
    "team": {
      "type": "pick",
      "choice": "billing",
      "probabilities": { "billing": 0.9112, "technical": 0.0301, "shipping": 0.0204, "other": 0.0383 },
      "confidence": 0.8729,
      "abstained": false
    },
    "urgency": {
      "type": "rate",
      "rating": 2.699,
      "levels": ["Can wait", "This week", "Within two working days", "Today"],
      "probabilities": [0.012, 0.061, 0.143, 0.784],
      "confidence": 0.5761,
      "abstained": false
    },
    "angry": { "type": "check", "probability": 0.9421, "confidence": 0.8842, "abstained": false }
  },
  "usage": {
    "input_tokens": 118,
    "state_tokens": 52,
    "question_tokens": 66,
    "charge_micro_cents": 590,
    "unit_price_micro_cents": 5,
    "tier": "t1"
  }
}
```

### The answers

| Field | What it tells you |
| --- | --- |
| `team.choice` | The most probable option: `billing`. |
| `team.probabilities` | One probability per option, in the order you sent them. They sum to exactly 1. |
| `team.confidence` | For a pick, the gap between the two largest probabilities: 0.9112 minus 0.0383 is 0.8729. |
| `team.abstained` | `false`, because 0.8729 is not below your `min_confidence` of 0.5. |
| `urgency.rating` | The probability-weighted level index, from 0 (`Can wait`) to 3 (`Today`). 2.699 sits between `Within two working days` and `Today`, close to `Today`. |
| `urgency.probabilities` | One probability per level, aligned with `levels`. |
| `urgency.confidence` | How concentrated the probabilities are around the rating. 1 means all on one level. |
| `urgency.abstained` | Always `false` here, because this question has no `min_confidence`. |
| `angry.probability` | The probability that the answer is yes: 0.9421. |
| `angry.confidence` | For a check, the distance from 50/50: two times 0.9421 minus 1 is 0.8842. |

Confidence says how concentrated an answer is. It is not a promise that the answer is right. See [Confidence](/docs/concepts/confidence/).

### The envelope

| Field | What it tells you |
| --- | --- |
| `id` | The request id. The same value is in the `x-request-id` header. Quote it to support. |
| `model` | The exact version that answered. You asked for the alias `dex-1`, and the response names the version it resolved to. See [Models and versions](/docs/concepts/models/). |
| `served_by` | `gpu` for our own engine, `fallback` for the hosted fallback. See [Fallback and served_by](/docs/concepts/fallback/). |
| `calibration` | The calibration version applied to the probabilities. Each one has a public report on the [calibration page](/docs/calibration/). |
| `usage.input_tokens` | The billed tokens: `state_tokens` (52) plus `question_tokens` (66). Output is free. |
| `usage.charge_micro_cents` | What was debited: `input_tokens` times `unit_price_micro_cents`, here 118 times 5, is 590 micro-cents, which is EUR 0.0000059. One euro is 100,000,000 micro-cents. Test keys are charged 0. See [Tokens and billing](/docs/reference/billing/). |

## Act on it

Use an answer only when it did not abstain. When `team` abstains, Dex is telling you that two teams are close, so send the ticket to a person instead of guessing. `route_to_person` and the other functions below stand for your own code.

```bash tab="curl"
curl -s https://api.thinqit.ai/v1/decide \
  -H "authorization: Bearer $DEX_API_KEY" \
  -H "content-type: application/json" \
  -d @request.json > response.json

# Prints "person" when the team answer abstained, else the team
jq -r 'if .answers.team.abstained then "person" else .answers.team.choice end' response.json
```

```python tab="Python"
team = decision.pick("team")
if team.abstained:
    route_to_person(ticket)  # not sure enough: a person decides
else:
    route_to_team(ticket, team.choice)

if decision.rate("urgency").rating >= 2.5:
    set_priority(ticket, "high")

angry = decision.check("angry")
if not angry.abstained and angry.probability >= 0.5:
    add_tag(ticket, "angry")
```

```ts tab="TypeScript"
const { team, urgency, angry } = decision.answers;

if (team.abstained) {
  routeToPerson(ticket); // not sure enough: a person decides
} else {
  routeToTeam(ticket, team.choice);
}

if (urgency.rating >= 2.5) setPriority(ticket, "high");

if (!angry.abstained && angry.probability >= 0.5) addTag(ticket, "angry");
```

If a call fails, the body has an error `type` and `code`. See [Errors](/docs/reference/errors/#status-codes) for which ones to retry.

## Next steps

- Learn the three [question types](/docs/concepts/questions/) and how to write good options and levels.
- Choose a threshold with [Abstention](/docs/concepts/abstention/).
- Read the [Known limits](/docs/concepts/known-limits/) before you automate: instructions hidden in the state, sarcasm, date arithmetic and Dutch criteria.
- Pin a version for tests with [Determinism](/docs/concepts/determinism/).
- Retry safely with [Idempotency](/docs/reference/idempotency/).
- Check the [limits](/docs/reference/limits/) and [rate limits](/docs/reference/rate-limits/).
- Read the full schema in the [API reference for decide](/docs/api/decide/).
