# How do I list, inspect and load a VARTA agent?

List the agents visible to your tenant, read one agent's resolved runtime stack, or load an agent to open a session — one call that returns the session id, the pre-synthesised opening line and the providers that will actually run.


An agent is what you built in the VARTA studio: a language model, a speech recogniser,
a speech synthesiser, a voice and a conversation flow, bundled under one id. `GET
/v1/agents` and `GET /v1/agents/{agent_id}` read that configuration back; `POST
/v1/agents/{agent_id}/load` is where a conversation actually begins — it opens a
session and returns the opening line with its audio already synthesised, so your
code never assembles a pipeline or a greeting itself.

> **Tenant isolation on this page applies in enforce mode.** The scoping described
> below — "visible to your tenant", and an id belonging to another tenant reported as
> [`not_found`](/docs/errors/not_found) — is enforced only when the instance runs with
> `auth_mode: "enforce"`. On a **default instance that setting is `"audit"`**
> (`backend/app/core/config.py:26`), and in audit mode `require_tenant` returns the
> resource without comparing tenants at all
> (`backend/app/security/ownership.py:51-52`). Object-level isolation is then
> *observed but not enforced*: because `GET /v1/agents` filters purely on that check
> (`backend/app/api/v1/agents.py:90-102`), on a default instance it lists **every
> tenant's agents**, not just yours. See [Authentication](/docs/authentication).

## List agents

```varta-endpoint
GET /v1/agents
```

Returns every agent visible to your tenant, sorted by id.

```bash
curl "$VARTA_BASE_URL/agents" \
  -H "Authorization: Bearer $VARTA_API_KEY"
```

```json
{
  "data": [
    {
      "id": "ag_412",
      "name": "Collections — Hindi",
      "language": "hi",
      "voice_id": "voice_7a1",
      "agent_name": "Priya",
      "agent_gender": "female",
      "llm": { "provider": "openai", "model": "gpt-4o-mini", "source": "agent" },
      "stt": { "provider": null, "source": "deployment_default" },
      "tts": { "provider": "elevenlabs", "voice_id": "voice_7a1", "source": "agent" },
      "steps": 9
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

`llm`, `stt` and `tts` each carry a `source`: `"agent"` when the agent overrides that
provider, `"deployment_default"` when it falls back to whatever this deployment runs
by default. A `null` provider with `source: "deployment_default"` is normal, not a
misconfiguration — it means the agent doesn't pin one.

`has_more` and `next_cursor` are always `false` / `null` today: this endpoint does not
yet paginate.

**Errors:** none specific to this request.

## Get an agent

```varta-endpoint
GET /v1/agents/{agent_id}
```

```bash
curl "$VARTA_BASE_URL/agents/ag_412" \
  -H "Authorization: Bearer $VARTA_API_KEY"
```

Returns the same shape as one item in the `GET /v1/agents` list above.

**Errors:** [`workflow_not_found`](/docs/errors/workflow_not_found) when `agent_id`
isn't a value this API ever issued, [`not_found`](/docs/errors/not_found) when it's a
well-formed id that doesn't exist for your tenant — including one that belongs to
another tenant, which is deliberately reported the same way so a cross-tenant id
can't be distinguished from a missing one.

## Load an agent

```varta-endpoint
POST /v1/agents/{agent_id}/load
```

Loading an agent is one call. It resolves the agent's model, speech recognition,
speech synthesis, voice and flow, opens a session, and returns the opening line —
already synthesised — along with filler clips and the resolved runtime stack.

```bash
curl -X POST "$VARTA_BASE_URL/agents/ag_412/load" \
  -H "Authorization: Bearer $VARTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customer_name": "Rahul", "context": {"order_id": "SO-4821"}}'
```

Every field in the body is optional. `context` is passed through to the pre-call
context initialiser, so the opening line can already name the caller.

```json
{
  "session_id": "se_9021",
  "agent_id": "ag_412",
  "opening": {
    "text": "Namaste Rahul, main Priya baat kar rahi hoon...",
    "audio_url": "https://.../opening_9021.mp3",
    "clips": [
      {
        "intent_key": "opening_greeting",
        "display_order": 0,
        "text": "Namaste Rahul, main Priya baat kar rahi hoon...",
        "audio_url": "https://.../opening_9021.mp3",
        "livekit_wav_url": null,
        "requires_user_input": false
      }
    ]
  },
  "filler_clips": {},
  "behavior_clips": {},
  "runtime": {
    "agent_name": "Priya",
    "language": "hi",
    "llm": { "provider": "openai", "model": "gpt-4o-mini", "source": "agent" },
    "stt": { "provider": null, "source": "deployment_default" },
    "tts": { "provider": "elevenlabs", "voice_id": "voice_7a1", "source": "agent" },
    "steps": 9
  },
  "ended": false
}
```

Hold on to `session_id` — it's what you send to every endpoint on the
[Sessions](/docs/api/sessions) page next. `filler_clips` and `behavior_clips` are
pre-synthesised clips your client can play opportunistically (a filler while waiting
on a turn, a scenario-specific reaction); both are commonly empty objects and that's
not an error, it just means this agent has none configured.

A clip object's documented fields are `intent_key`, `display_order`, `text`,
`audio_url`, `livekit_wav_url` and `requires_user_input`. `livekit_wav_url` is a
WAV-format rendition of the same clip — the telephony path plays this one — but it is
populated for every caller regardless of channel, not only for SIP calls; it may be
`null`. The runtime attaches other fields to clips internally (for example
`_played`) — anything not listed here is implementation detail, not part of the
contract.

Like every state-changing call under `/v1`, this endpoint honours an
`Idempotency-Key` header — see [Idempotency](/docs/idempotency) for the replay
semantics; [Calls](/docs/api/calls#place-a-call) explains why it matters most for a
call that actually dials.

**Errors:** [`invalid_request`](/docs/errors/invalid_request) when the body isn't a
JSON object or `context` isn't, [`workflow_not_found`](/docs/errors/workflow_not_found)
when `agent_id` is malformed, [`not_found`](/docs/errors/not_found) when it's a
well-formed id this tenant doesn't own.
