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

# Quickstart

> From an API key to your first answers in five steps.

export const appUrl = "https://app.vainona.ai";

These are the same five steps as the onboarding page in the dashboard, which can also run them for you. You need an API key: sign-up creates a read-write key scoped to `default/*`, and you can create more on the dashboard's Keys page.

`default/quickstart` is a free place to try things. It holds up to 10,000 documents and 100 MB (a write past that is refused with `too_large`), and its judging pauses for the month at a small usage limit ([pricing](/pricing#quickstart)). For real data, write to a namespace of your own.

<Steps>
  <Step title="Install the SDK">
    <CodeGroup>
      ```sh Python theme={null}
      pip install vainona
      ```

      ```sh TypeScript theme={null}
      npm install vainona
      ```
    </CodeGroup>
  </Step>

  <Step title="Create a client">
    <CodeGroup>
      ```python Python theme={null}
      import os
      from vainona import Client

      db = Client(api_key=os.environ["VAINONA_API_KEY"])
      ns = db.namespace("default/quickstart")
      ```

      ```ts TypeScript theme={null}
      import { Client } from "vainona";

      const db = new Client({ apiKey: process.env.VAINONA_API_KEY! });
      const ns = db.namespace("default/quickstart");
      ```
    </CodeGroup>

    The namespace does not exist yet. It is created by its first write or judgment.
  </Step>

  <Step title="Define a judgment">
    <CodeGroup>
      ```python Python theme={null}
      ns.judgments.create(
          name="needs_escalation",
          type="bool",
          question="Does this ticket require a human to take over from the automated flow?",
          criteria="Escalate when the customer is at risk of leaving, mentions legal action, or the automation has failed twice.",
          context={"fields": ["state.subject", "state.body"]},
          engine={"name": "jev", "version": "current"},
          thresholds={"escalate": 0.85},
          freshness={"policy": "on_change"},
      )
      ```

      ```ts TypeScript theme={null}
      await ns.judgments.create({
        name: "needs_escalation",
        type: "bool",
        question: "Does this ticket require a human to take over from the automated flow?",
        criteria: "Escalate when the customer is at risk of leaving, mentions legal action, or the automation has failed twice.",
        context: { fields: ["state.subject", "state.body"] },
        engine: { name: "jev", version: "current" },
        thresholds: { escalate: 0.85 },
        freshness: { policy: "on_change" },
      });
      ```
    </CodeGroup>

    `on_change` computes the answer whenever a document changes, which you need in order to filter or sort on it. Jev `current` runs whichever Jev model the provider serves now; each answer records the epoch of model behaviour that produced it in `engine_version`, such as `current+2026-09-24.1`, and a new epoch starts only when we detect the model's behaviour change. See the [engines](/engines/index) page.
  </Step>

  <Step title="Write three documents">
    <CodeGroup>
      ```python Python theme={null}
      ns.write(
          upsert=[
              {"id": "t_1", "attributes": {"plan": "pro"}, "state": {"subject": "Cancel my account", "body": "Third outage this week. Talking to my lawyer."}},
              {"id": "t_2", "attributes": {"plan": "free"}, "state": {"subject": "Dark mode?", "body": "Is there a dark mode?"}},
              {"id": "t_3", "attributes": {"plan": "pro"}, "state": {"subject": "Invoice", "body": "The bot sent me the wrong invoice twice."}},
          ],
          wait_for=["needs_escalation"],
      )
      ```

      ```ts TypeScript theme={null}
      await ns.write({
        upsert: [
          { id: "t_1", attributes: { plan: "pro" }, state: { subject: "Cancel my account", body: "Third outage this week. Talking to my lawyer." } },
          { id: "t_2", attributes: { plan: "free" }, state: { subject: "Dark mode?", body: "Is there a dark mode?" } },
          { id: "t_3", attributes: { plan: "pro" }, state: { subject: "Invoice", body: "The bot sent me the wrong invoice twice." } },
        ],
        wait_for: ["needs_escalation"],
      });
      ```
    </CodeGroup>

    The write is durable once it returns. `wait_for` also waits until the answers exist, which usually takes a few seconds. Without it, the write returns in about 100 ms and the answers follow.
  </Step>

  <Step title="Query by the answer">
    <CodeGroup>
      ```python Python theme={null}
      page = ns.query(
          filters=("answers.needs_escalation.thresholds.escalate", "Eq", True),
          rank_by=("answers.needs_escalation.p", "desc"),
          include={"attributes": ["plan"], "answers": ["needs_escalation"]},
      )
      for row in page["rows"]:
          print(row["id"], row["answers"]["needs_escalation"])
      ```

      ```ts TypeScript theme={null}
      const { rows } = await ns.query({
        filters: ["answers.needs_escalation.thresholds.escalate", "Eq", true],
        rank_by: ["answers.needs_escalation.p", "desc"],
        include: { attributes: ["plan"], answers: ["needs_escalation"] },
      });
      for (const row of rows) console.log(row.id, row.answers?.needs_escalation);
      ```
    </CodeGroup>

    Each answer carries its probability `p`, the `escalate` threshold as a boolean, its freshness, and the revision, judgment version and engine version it was computed with.
  </Step>
</Steps>

## Next

* See the answers, their history and the exact text the engine saw in the dashboard at <a href={appUrl}>{appUrl}</a>.
* Learn what the [freshness](/concepts/freshness) states mean.
* Cut cost with a tighter [context recipe](/guides/context-recipes).
