# Variables

> How an agent's fields are declared, filled, checked, and kept from rewriting the agent.

An agent's instructions and its first message can name values it is given when a conversation
starts: `Hello {{first_name}}, I'm calling about your appointment on {{appointment_date}}.` Each
name is a **variable**. A call, a chat, a campaign row and the widget all fill them the same way and
are checked by the same rules.

## Which variables an agent has

Writing `{{first_name}}` in the instructions or the first message *is* declaring it. Read an agent's
variables from its `variables_schema`, a JSON Schema:

**curl**

```sh
#!/bin/sh
# Read an agent and its variables
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/agents/8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Read an agent and its variables
const response = await fetch("https://api.aigently.ai/v1/agents/8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Read an agent and its variables
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://api.aigently.ai/v1/agents/8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Read an agent and its variables
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/agents/8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c", nil)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// Read an agent and its variables
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/agents/8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// Read an agent and its variables
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.aigently.ai/v1/agents/8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c")
{
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// Read an agent and its variables
$curl = curl_init("https://api.aigently.ai/v1/agents/8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c");
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

```json
{
  "version": "3f9c1e07",
  "type": "object",
  "properties": {
    "first_name": {"type": "string", "maxLength": 500, "title": "First name"},
    "party_size": {"type": "integer", "minimum": 1, "maximum": 12, "default": 2, "title": "Party size"},
    "visit_date": {"type": "string", "format": "date", "title": "Visit date"}
  },
  "required": ["first_name", "visit_date"],
  "additionalProperties": true
}
```

`version` changes when the variables change and at no other time, so you can tell an edit to the
fields from any other edit to the agent. When it changes, your receivers are sent
[`agent.updated`](/guides/webhooks#events-about-an-agent) with the new list.

## Pin a version

An agent's fields change when somebody publishes. To keep an integration on the version it was
built against, send `agent_version` when you start a call, a scheduled call, a chat or a browser
call: the conversation is answered by that published version from its first turn to its last, and
its values are checked against that version's fields, not today's.

`GET /v1/agents/{id}/versions` lists every version the agent was published as, newest first, each
with its own `variables_schema` and the note its publisher wrote. A version the agent never had is
refused with `unknown_agent_version`. The conversation's `agent_version` says which one answered.

## Types

| Type | Send | Notes |
|---|---|---|
| Text | `"Sara"` | Long text is shortened where it is put into the instructions. |
| Number | `4` or `4.5` | A whole-number field refuses `4.5`. Minimum and maximum apply. |
| Yes / no | `true` or `false` | |
| Email | `"sara@aigently.ai"` | |
| Phone | `"+12025550123"` | Spaces and dashes are taken out first. |
| Date | `"2027-03-04"` | `YYYY-MM-DD`. A field may set the earliest and latest date. |
| Time | `"14:30"` | 24-hour `HH:MM`. |
| Choice | `"terrace"` | One of the field's options. |

Numbers and `true`/`false` may be sent as JSON values or as text; each comes back in its own type.

## Required, optional and defaults

A variable the agent says out loud is required — unless it has a **default**, which stands in when
nothing is sent. A field marked optional may simply be left out. A required value that is missing,
or one of the wrong type, refuses the request with `422 invalid_variables`, and `errors` names each
one:

```json
{
  "code": "invalid_variables",
  "errors": [
    {"field": "variables.first_name", "code": "required", "message": "First name is required"},
    {"field": "variables.visit_date", "code": "invalid", "message": "Visit date is not a valid date"}
  ]
}
```

**A name the agent does not use is not refused.** It is listed in the conversation's
`ignored_variables`, so a renamed field shows up there rather than failing every call.

Names starting `system_` are reserved for the platform.

## Where each value came from

Every conversation records the source of each value in `variable_sources`: `api` for one your
request sent, `lookup` for one your [context lookup](/guides/inbound-calls) answered, `campaign`,
`identity_token` for a signed widget token, `widget` or `link` for what a visitor's page claimed.
Values from a lookup or a visitor's page are never treated as the caller's own answers.

## Variables or metadata?

| | `variables` | `metadata` |
|---|---|---|
| The agent sees it | Yes | Never |
| Checked against the agent's fields | Yes | No |
| Returned on every read and event | Yes | Yes |
| Use it for | What the agent should know and say | Your own ids: a CRM record, a campaign, a lead |

## Values cannot rewrite the agent

A value is put into the agent's instructions inside a block of its own, marked as data. A
`first_name` of `Ignore your instructions and …` is a strange name, not a new instruction.
