# Test mode

> Build and test an integration without a phone number, a carrier or credit — and without ever ringing anybody.

A **test key** (`ag_test_…`) works like a live one against your real agents, with three differences:
its calls ring nobody, it sees only test data, and its events go only to test receivers.

## Test numbers

A test key calls only these numbers, and each one plays out a fixed ending over about ten seconds —
with the agent's real greeting and your values, the same events a real call sends, and, where the
call is answered, its analysis and a short sample recording.

| Number | What it plays | Analysis checks |
|---|---|---|
| `+12025550100` | Answered, talked, and somebody hung up | pass |
| `+12025550101` | Answered, talked, and somebody hung up | fail |
| `+12025550102` | The line was busy | — |
| `+12025550103` | It rang and nobody answered | — |
| `+12025550104` | The person rejected the call | — |
| `+12025550105` | An answering machine answered, and the agent left its message | — |
| `+12025550106` | An answering machine answered, and no message was left | — |
| `+12025550107` | The carrier would not put the call through | — |

Any other number is refused with `422 test_number_required`. Nothing is dialled and nothing is
charged.

## Calls to your agents

A test key can also make a call *to* one of your inbound agents, from a test number — the only way to
try an agent's [context lookup](/guides/inbound-calls) without a phone:

**curl**

```sh
#!/bin/sh
# Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/test/inbound-calls" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY" \
  --json '{
  "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
  "from_number": "+12025550100"
}'
```

**JavaScript**

```js
// Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
const response = await fetch("https://api.aigently.ai/v1/test/inbound-calls", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
    "from_number": "+12025550100"
  }),
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://api.aigently.ai/v1/test/inbound-calls",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
    json={
        "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
        "from_number": "+12025550100",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
package main

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

func main() {
	payload := []byte(`{
  "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
  "from_number": "+12025550100"
}`)
	request, err := http.NewRequest("POST", "https://api.aigently.ai/v1/test/inbound-calls", bytes.NewReader(payload))
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	request.Header.Set("Content-Type", "application/json")
	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
// Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
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 {
        String body = """
            {
              "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
              "from_number": "+12025550100"
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/test/inbound-calls"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .header("Content-Type", "application/json")
            .method("POST", HttpRequest.BodyPublishers.ofString(body))
            .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
// Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
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.Post, "https://api.aigently.ai/v1/test/inbound-calls")
{
    Content = new StringContent(
        """
        {
          "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
          "from_number": "+12025550100"
        }
        """,
        Encoding.UTF8,
        "application/json"),
};
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
// Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
$curl = curl_init("https://api.aigently.ai/v1/test/inbound-calls");
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "agent_id" => "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
        "from_number" => "+12025550100",
    ]),
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

The agent's **test address** is asked, or its sample answer used, and never its live address. The
answer says what the lookup made of it, and the call then plays out like any test call.

## Chats

A chat with a test key uses the agent's real model, because the reply is the point of testing it,
and is charged like any chat. It is marked as a test-key chat on the organization's transactions.

## What stays apart

- A test key reads only test-key conversations. A real conversation asked for with a test key is
  `404`, and a live key never sees test-key conversations.
- Test-key conversations go only to **test** webhook receivers, and never count in the console's
  results or dashboards. The console's call list shows them when **Mode** is set to **Test key**.
- They are deleted after 30 days.
- A test key makes at most ten test calls a minute.

## Moving to live

The same code works with a live key. See [Going live](/going-live) for what changes.
