# One-shot answers

> Ask your agent a single question with POST /v1/agents/{agent}/ask — with identity, caller-supplied history, actions, privacy mode, overrides and handoffs.

A one-shot `ask` sends one question and gets one answer, with no session to manage. The agent uses all of its settings — instructions, knowledge, tools and guardrails — so answers are the same quality as in a chat. Use it for help buttons inside your app, answers on product pages, drafting replies for your team, or any backend job that needs the agent's knowledge.

## A basic call

**curl**

```bash
curl https://api.k-agent.kerneltics.com/v1/agents/store-assistant/ask \
  -H "Authorization: Bearer $KAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input": "How much is delivery?"}'
```

**JavaScript**

```js
const res = await fetch('https://api.k-agent.kerneltics.com/v1/agents/store-assistant/ask', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.KAGENT_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ input: 'How much is delivery?' }),
});
const run = await res.json();
if (!res.ok) throw new Error(`${run.error.code}: ${run.error.message}`);

if (run.outcome === 'handed_off') {
  console.log('Passed to the team:', run.handoff.summary);
}
console.log(run.output_text);
```

**Python**

```python
import os, requests

r = requests.post(
    "https://api.k-agent.kerneltics.com/v1/agents/store-assistant/ask",
    headers={"Authorization": f"Bearer {os.environ['KAGENT_API_KEY']}"},
    json={"input": "How much is delivery?"},
    timeout=120,
)
run = r.json()
if not r.ok:
    raise RuntimeError(f"{run['error']['code']}: {run['error']['message']}")
if run["outcome"] == "handed_off":
    print("Passed to the team:", run["handoff"]["summary"])
print(run["output_text"])
```

The response is a [run](/docs/en/concepts/runs-and-streaming/#the-run-object). Read `output_text` for the answer and `outcome` to know whether the agent `answered` or `handed_off`.

## Request fields

| Field | Type | Description |
|---|---|---|
| `input` | string or `[{type: "text", text}]` | The question. Up to 32,000 characters with a secret key, 4,000 with browser credentials. |
| `end_user` | object | `{external_id, name?, traits?}`. With a secret key the user is **verified**, which enables identity-bound tools and per-user memory. |
| `history` | array | Earlier turns from your own app, e.g. `[{"role": "user", "content": "…"}, {"role": "assistant", "content": "…"}]`. Used as text and marked as unverified, caller-supplied context. |
| `variables` | object | Values for the agent's [variables](/docs/en/concepts/settings/#variables), including secret ones (used for this call only). |
| `allow_actions` | boolean | Default `false`: tools with side effects are not offered. Set `true` to allow them. |
| `store` | boolean | Default `true`. `false` keeps no transcript (see below). |
| `stream` | boolean | Stream the answer as [Server-Sent Events](/docs/en/concepts/runs-and-streaming/#streaming). |
| `version` | integer | A published version to use instead of the latest. |
| `overrides` | object | Per-request changes the agent allows (see below). |
| `metadata` | object | Your own key–value pairs, returned on the run. |
| `wait_seconds` | integer | How long to wait for the answer (default 60, max 110) before returning `202`. |

## Who is asking

Pass `end_user` when you know the person. Verified one-shot calls can use HTTP tools that need identity — for example looking up *this* customer's order — and they share the action memory and limits of that end user's sessions:

```json
{
  "input": "Where is my order?",
  "end_user": { "external_id": "cus_1042", "name": "Fahad", "traits": { "plan": "gold" } }
}
```

## Actions are off unless you ask

Because one-shot calls are often automated, tools with side effects (`create_ticket`, HTTP tools with `effect: "action"`, client tools) are removed by default. Read tools and handoffs always work. Send `"allow_actions": true` when the call should be able to act.

## Private mode: `store: false`

With `"store": false`:

- no messages are stored, and the run's events are deleted when it ends;
- run steps keep no arguments, results or summaries;
- no `message.created` event is emitted, and webhooks carry no text;
- `GET /v1/runs/{run}` returns only the status and usage;
- client tools are not offered.

Usage is still recorded for billing.

## Per-request overrides

An agent can allow some settings to change per call. List them in the agent's `overrides.allowed` (and, for `model`, the models in `overrides.models`), then send:

```json
{
  "input": "Summarize our return policy.",
  "overrides": {
    "dialect": "msa",
    "tone": "formal",
    "instructions_append": "Answer in at most three sentences."
  }
}
```

Allowed fields are `dialect`, `tone`, `instructions_append`, `temperature`, `model`, `tools_disable` and `history_limit`. The never-handle list, the escalation reply and tool grants can never be overridden, and only secret keys may send overrides; anything else returns `422 override_not_allowed`.

## When the agent hands over

Guardrails apply to one-shot calls too. A never-handle topic, or a question the agent cannot answer from what it knows, ends with a handoff that your team can see:

```json
{
  "object": "run",
  "mode": "one_shot",
  "status": "completed",
  "outcome": "handed_off",
  "output_text": "Thank you for telling us. We've passed this to the responsible manager, who will contact you shortly.",
  "handoff": { "id": "ho_01k6rz6c8e0g2j4m6p8r0t2v4x", "reason_type": "liability", "summary": "Customer reports the perfume caused a skin reaction", "status": "recorded" }
}
```

- For a liability case with an escalation reply configured, `output_text` is your reply, word for word.
- There is no session to pause, so the handoff is `recorded`. It appears in the Handoff Desk under the **One-shot** filter, and the `handoff.requested` webhook fires with the `run_id` — use it to follow up through your own channel.
- Handed-off runs are not billed.

## Errors to handle

| Status and code | When |
|---|---|
| `409 agent_not_ready` | The agent can't answer yet (for example no model key). Check `readiness.blockers`. |
| `413 input_too_large` | The input is too long. |
| `422 variable_missing`, `variable_unknown` | Variables don't match the agent's declarations. |
| `429 quota_exceeded` | The Free plan's monthly AI conversations are used up (`x-should-retry: false`). |
| `429 cost_cap_exceeded` | The organization's daily cost cap was reached. |

## Billing

A one-shot ask costs **0.25 × the model weight** for each started block of 50,000 input tokens — for a typical question, 0.25 of an AI conversation. See [Usage and billing](/docs/en/concepts/usage-and-billing/).
