# Going live

> What changes when the same code runs with a live key.

The code you wrote against a test key is the code you run live: the address, the requests and the
answers are the same. What changes is that calls ring real phones, conversations cost credit, and
your receivers hear about real people.

## Before the first live call

- **A live key.** On **API keys**, make one with **Live**, limited to the projects — or the agents —
  it needs. Asking for it takes your password again, and every owner of the organization is
  emailed. A live key never works from a browser: keep it on your server.
- **A number to call from.** An agent that calls out needs a verified number on its
  **Connect → Caller ID** tab. Without one, a call is refused with `caller_id_unavailable`.
- **Countries.** The deployment calls only the countries it allows, never emergency or service
  numbers, and premium-rate numbers only where it has said so. Anything else is refused with
  `destination_not_allowed`. Send numbers in full international format: `+`, the country code,
  then the number.
- **Consent.** Call people who agreed to hear from you, at hours that suit them. The platform
  records who placed every call — the key's name is on it in the audit log.
- **Live receivers.** A webhook receiver is either live or test. Add a live one for your
  production server; your test receivers keep getting test events only.

## Limits

| Limit | Default | When it is reached |
|---|---|---|
| Calls at the same time, per organization | 10 | `429 concurrency_limit_reached` |
| Calls a day, per organization | 2,000 | `429 daily_limit_reached` |
| Requests a minute, per key | 600 reads, 120 writes, 1,200 chat messages | `429 rate_limited` |

`GET /v1/me` shows your key's limits and how many of your calls are in progress. Our staff can
raise an organization's limits. See [Rate limits](/guides/rate-limits).

**curl**

```sh
#!/bin/sh
# Check your key
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/me" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Check your key
const response = await fetch("https://api.aigently.ai/v1/me", {
  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
# Check your key
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://api.aigently.ai/v1/me",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Check your key
package main

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

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/me", 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
// Check your key
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/me"))
            .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
// Check your key
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/me")
{
};
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
// Check your key
$curl = curl_init("https://api.aigently.ai/v1/me");
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";
```

## Credit

Live conversations are charged from the organization's credit, as conversations from the console
are; the API itself costs nothing. When the credit runs out, new calls and chats are refused with
`402 insufficient_credit`, and reading conversations keeps working.

## Keep it safe

- Rotate a key that may have leaked — the old secret keeps working for the time you choose, so
  your servers can be updated first. Revoke a key nobody uses.
- Check every webhook's signature before you act on it ([how](/guides/webhooks#check-the-signature)).
- Build each call's `Idempotency-Key` from your own data, so a retry can never call twice.
