# Outbound calls

> Place a call with an agent, follow it, end it, and know what each ending means.

An outbound agent calls a number you give it, with values you give it, and you hear about every
step. A call needs the `calls:write` permission.

## Place a call

**curl**

```sh
#!/bin/sh
# Place a call. With a test key this calls a test number and rings nobody.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/calls" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY" \
  -H "Idempotency-Key: lead-20931" \
  --json '{
  "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
  "to_number": "+12025550100",
  "variables": {
    "first_name": "Sara"
  },
  "metadata": {
    "crm_lead_id": "L-20931"
  },
  "external_id": "lead-20931"
}'
```

**JavaScript**

```js
// Place a call. With a test key this calls a test number and rings nobody.
const response = await fetch("https://api.aigently.ai/v1/calls", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "lead-20931",
  },
  body: JSON.stringify({
    "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
    "to_number": "+12025550100",
    "variables": {
      "first_name": "Sara"
    },
    "metadata": {
      "crm_lead_id": "L-20931"
    },
    "external_id": "lead-20931"
  }),
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Place a call. With a test key this calls a test number and rings nobody.
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://api.aigently.ai/v1/calls",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
        "Idempotency-Key": "lead-20931",
    },
    json={
        "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
        "to_number": "+12025550100",
        "variables": {
            "first_name": "Sara",
        },
        "metadata": {
            "crm_lead_id": "L-20931",
        },
        "external_id": "lead-20931",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Place a call. With a test key this calls a test number and rings nobody.
package main

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

func main() {
	payload := []byte(`{
  "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
  "to_number": "+12025550100",
  "variables": {
    "first_name": "Sara"
  },
  "metadata": {
    "crm_lead_id": "L-20931"
  },
  "external_id": "lead-20931"
}`)
	request, err := http.NewRequest("POST", "https://api.aigently.ai/v1/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")
	request.Header.Set("Idempotency-Key", "lead-20931")
	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
// Place a call. With a test key this calls a test number and rings nobody.
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": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
              "to_number": "+12025550100",
              "variables": {
                "first_name": "Sara"
              },
              "metadata": {
                "crm_lead_id": "L-20931"
              },
              "external_id": "lead-20931"
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/calls"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .header("Content-Type", "application/json")
            .header("Idempotency-Key", "lead-20931")
            .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
// Place a call. With a test key this calls a test number and rings nobody.
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/calls")
{
    Content = new StringContent(
        """
        {
          "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
          "to_number": "+12025550100",
          "variables": {
            "first_name": "Sara"
          },
          "metadata": {
            "crm_lead_id": "L-20931"
          },
          "external_id": "lead-20931"
        }
        """,
        Encoding.UTF8,
        "application/json"),
};
request.Headers.Add("Idempotency-Key", "lead-20931");
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
// Place a call. With a test key this calls a test number and rings nobody.
$curl = curl_init("https://api.aigently.ai/v1/calls");
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
        "Content-Type: application/json",
        "Idempotency-Key: lead-20931",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "agent_id" => "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
        "to_number" => "+12025550100",
        "variables" => [
            "first_name" => "Sara",
        ],
        "metadata" => [
            "crm_lead_id" => "L-20931",
        ],
        "external_id" => "lead-20931",
    ]),
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

| Field | What it is |
|---|---|
| `agent_id` | One of your outbound agents. It must be published. |
| `to_number` | International format: `+`, the country code, then the number. |
| `variables` | Values for the agent's fields — see [Variables](/guides/variables). |
| `metadata` | Your own data, up to 20 keys. The agent never sees it; you get it back on every read and every event. |
| `external_id` | Your own id for the conversation — a lead, an order. Find it again with `GET /v1/conversations?external_id=`. |
| `from_number` or `phone_number_id` | Which of the agent's numbers to call from. Leave both out and the agent's own caller ID is used. |
| `language` | One of the agent's language versions, like `ar`. |
| `call_window` | The hours the call may start in — see [Calling hours](#calling-hours). |

**`Idempotency-Key` is required.** It is your own id for this call, up to 120 characters — build it
from your data, like `lead-8812-attempt-2`. The same request with the same key returns the same
conversation, with `Idempotent-Replayed: true`, and never calls twice; the same key with a different
request is refused with `idempotency_key_reused`. See [Safe retries](/guides/idempotency).

The answer is `201` with the [conversation](/reference/objects/Conversation), `pending` until the
phone is answered. Everything a call can be refused for is checked before anything is dialled.

## Follow it

Either listen for [webhooks](/guides/webhooks) — `conversation.started` when it is answered and
`conversation.ended` exactly once when it ends, answered or not — or read the conversation:

**curl**

```sh
#!/bin/sh
# Read a conversation with its transcript
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d?include=transcript" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Read a conversation with its transcript
const response = await fetch("https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d?include=transcript", {
  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 a conversation with its transcript
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
    params={
        "include": "transcript",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Read a conversation with its transcript
package main

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

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d?include=transcript", 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 a conversation with its transcript
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/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d?include=transcript"))
            .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 a conversation with its transcript
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/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d?include=transcript")
{
};
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 a conversation with its transcript
$curl = curl_init("https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d?include=transcript");
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";
```

`status` is `pending`, then `active` once answered, and finally `completed` or `failed`. The
`revision` only goes up, so of two copies of a conversation the higher one is the newer.

## How a call ends

Every way a call can end, what the conversation says about it, and which events you get:

| What happened | `status` | `disposition` | `ended_by` | Events | Call again? |
|---|---|---|---|---|---|
| Answered, talked, and somebody hung up | `completed` | `answered` | `caller` or `agent` | `conversation.started`, `conversation.ended`, `conversation.analyzed`, `conversation.recording_ready` | No. |
| Ended with /end after it was answered | `completed` | `answered` | `api` | `conversation.started`, `conversation.ended`, `conversation.analyzed` | No. |
| An answering machine answered, and the agent left its message | `completed` | `voicemail_left` | `agent` | `conversation.started`, `conversation.ended` | Your choice. |
| An answering machine answered, and no message was left | `failed` | `voicemail` | null | `conversation.started`, `conversation.ended` | Yes, later. |
| The line was busy | `failed` | `busy` | null | `conversation.ended` | Yes, later. |
| It rang and nobody answered | `failed` | `no_answer` | null | `conversation.ended` | Yes, later. |
| The person rejected the call | `failed` | `declined` | null | `conversation.ended` | Usually not. |
| The carrier would not put the call through | `failed` | `unreachable` | `failure` | `conversation.ended` | Check the number first. |
| The call could not be placed at all | `failed` | — | `failure` | `conversation.ended` | Yes, in a moment. |
| Cancelled with /end while it was ringing | `failed` | `no_answer` | `api` | `conversation.ended` | No — you cancelled it. |
| Marked failed when its report was late, then reported as a full conversation | `completed` | `answered` | `caller` or `agent` | `conversation.started`, `conversation.ended`, `conversation.updated`, `conversation.analyzed` | No — always keep the highest revision. |

- `conversation.analyzed` arrives only for an agent that analyses its conversations, and a little after the end.
- `conversation.recording_ready` arrives only for a call that was recorded.
- Events can arrive out of order. Keep the one with the highest `revision`.

An answering machine is handled by the agent's own voicemail setting: it leaves its message, or
hangs up and the call ends `voicemail`.

## End it yourself

**curl**

```sh
#!/bin/sh
# End a call or a chat
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d/end" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// End a call or a chat
const response = await fetch("https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d/end", {
  method: "POST",
  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
# End a call or a chat
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d/end",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// End a call or a chat
package main

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

func main() {
	request, err := http.NewRequest("POST", "https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d/end", 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
// End a call or a chat
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/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d/end"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("POST", 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
// End a call or a chat
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/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d/end")
{
};
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
// End a call or a chat
$curl = curl_init("https://api.aigently.ai/v1/conversations/5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d/end");
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
    CURLOPT_POSTFIELDS => "",
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

A call still ringing is cancelled; one in progress ends with the agent's own goodbye. Ending always
answers `200` with the conversation, so it is safe to send again.

## Calling hours

Name the hours a call may start in, in the time zone of the person you are calling:

```json
"call_window": {
  "timezone": "America/New_York",
  "start": "09:00",
  "end": "17:00",
  "days": ["mon", "tue", "wed", "thu", "fri"]
}
```

`timezone` is a name like `Asia/Riyadh`. The window includes `start` and ends just before `end`.
Leave `days` out for every day of the week. A call sent outside its window is refused with
`outside_call_window`, and `detail` says when the window opens next — so schedule it for then.

## Schedule a call for later

**curl**

```sh
#!/bin/sh
# Schedule a call inside calling hours. It is placed at the first moment the window is open at or after dial_at.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/scheduled-calls" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY" \
  -H "Idempotency-Key: reminder-20931" \
  --json '{
  "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
  "to_number": "+12025550100",
  "dial_at": "2026-10-12T13:00:00Z",
  "call_window": {
    "timezone": "America/New_York",
    "start": "09:00",
    "end": "17:00",
    "days": [
      "mon",
      "tue",
      "wed",
      "thu",
      "fri"
    ]
  },
  "variables": {
    "first_name": "Sara"
  },
  "external_id": "reminder-20931"
}'
```

**JavaScript**

```js
// Schedule a call inside calling hours. It is placed at the first moment the window is open at or after dial_at.
const response = await fetch("https://api.aigently.ai/v1/scheduled-calls", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "reminder-20931",
  },
  body: JSON.stringify({
    "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
    "to_number": "+12025550100",
    "dial_at": "2026-10-12T13:00:00Z",
    "call_window": {
      "timezone": "America/New_York",
      "start": "09:00",
      "end": "17:00",
      "days": [
        "mon",
        "tue",
        "wed",
        "thu",
        "fri"
      ]
    },
    "variables": {
      "first_name": "Sara"
    },
    "external_id": "reminder-20931"
  }),
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Schedule a call inside calling hours. It is placed at the first moment the window is open at or after dial_at.
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://api.aigently.ai/v1/scheduled-calls",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
        "Idempotency-Key": "reminder-20931",
    },
    json={
        "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
        "to_number": "+12025550100",
        "dial_at": "2026-10-12T13:00:00Z",
        "call_window": {
            "timezone": "America/New_York",
            "start": "09:00",
            "end": "17:00",
            "days": ["mon", "tue", "wed", "thu", "fri"],
        },
        "variables": {
            "first_name": "Sara",
        },
        "external_id": "reminder-20931",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Schedule a call inside calling hours. It is placed at the first moment the window is open at or after dial_at.
package main

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

func main() {
	payload := []byte(`{
  "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
  "to_number": "+12025550100",
  "dial_at": "2026-10-12T13:00:00Z",
  "call_window": {
    "timezone": "America/New_York",
    "start": "09:00",
    "end": "17:00",
    "days": [
      "mon",
      "tue",
      "wed",
      "thu",
      "fri"
    ]
  },
  "variables": {
    "first_name": "Sara"
  },
  "external_id": "reminder-20931"
}`)
	request, err := http.NewRequest("POST", "https://api.aigently.ai/v1/scheduled-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")
	request.Header.Set("Idempotency-Key", "reminder-20931")
	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
// Schedule a call inside calling hours. It is placed at the first moment the window is open at or after dial_at.
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": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
              "to_number": "+12025550100",
              "dial_at": "2026-10-12T13:00:00Z",
              "call_window": {
                "timezone": "America/New_York",
                "start": "09:00",
                "end": "17:00",
                "days": [
                  "mon",
                  "tue",
                  "wed",
                  "thu",
                  "fri"
                ]
              },
              "variables": {
                "first_name": "Sara"
              },
              "external_id": "reminder-20931"
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/scheduled-calls"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .header("Content-Type", "application/json")
            .header("Idempotency-Key", "reminder-20931")
            .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
// Schedule a call inside calling hours. It is placed at the first moment the window is open at or after dial_at.
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/scheduled-calls")
{
    Content = new StringContent(
        """
        {
          "agent_id": "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
          "to_number": "+12025550100",
          "dial_at": "2026-10-12T13:00:00Z",
          "call_window": {
            "timezone": "America/New_York",
            "start": "09:00",
            "end": "17:00",
            "days": [
              "mon",
              "tue",
              "wed",
              "thu",
              "fri"
            ]
          },
          "variables": {
            "first_name": "Sara"
          },
          "external_id": "reminder-20931"
        }
        """,
        Encoding.UTF8,
        "application/json"),
};
request.Headers.Add("Idempotency-Key", "reminder-20931");
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
// Schedule a call inside calling hours. It is placed at the first moment the window is open at or after dial_at.
$curl = curl_init("https://api.aigently.ai/v1/scheduled-calls");
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
        "Content-Type: application/json",
        "Idempotency-Key: reminder-20931",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "agent_id" => "8a1f3c2e-0b6d-4f5e-9a7c-2d4e6f8a0b1c",
        "to_number" => "+12025550100",
        "dial_at" => "2026-10-12T13:00:00Z",
        "call_window" => [
            "timezone" => "America/New_York",
            "start" => "09:00",
            "end" => "17:00",
            "days" => ["mon", "tue", "wed", "thu", "fri"],
        ],
        "variables" => [
            "first_name" => "Sara",
        ],
        "external_id" => "reminder-20931",
    ]),
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

The body is the one a call takes, plus `dial_at`: the earliest the call may be placed, at most 30
days ahead. Leave it out, or send a time already past, and the call is placed now. With a
`call_window`, it is placed at the first moment the window is open at or after `dial_at`, and the
`dial_at` in the answer is already moved there. `Idempotency-Key` is required, as it is for a call.

Everything a call can be refused for is checked when you schedule it, so a mistake is said at once,
and checked again when it is placed, because an agent can be unpublished or a number listed as
do-not-call in the meantime. If the window has closed by then, the call waits for its next opening.
If every line is busy, or the organization has placed today's calls, it waits and tries again: a
minute later for a busy line, an hour later for the day's limit.

A [scheduled call](/reference/objects/ScheduledCall) is `scheduled` until its time comes, and then:

| `status` | What happened |
|---|---|
| `placed` | It became a conversation. `conversation_id` names it, and it sends the usual events. |
| `failed` | It was refused when its time came, and no call was made. `error_code` says why — see below. |
| `cancelled` | You called it off. |

`error_code` on a failed call is the code `POST /v1/calls` would have answered, like
`agent_not_published` or `do_not_call`, or one of two more: `key_revoked` when the key that
scheduled it no longer works, and `lines_busy` when it was put off 30 times because every line was
busy or the day's calls were used up.

List the calls still waiting:

**curl**

```sh
#!/bin/sh
# List the calls still waiting
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/scheduled-calls?status=scheduled" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// List the calls still waiting
const response = await fetch("https://api.aigently.ai/v1/scheduled-calls?status=scheduled", {
  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
# List the calls still waiting
# pip install httpx
import os

import httpx

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

**Go**

```go
// List the calls still waiting
package main

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

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/scheduled-calls?status=scheduled", 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
// List the calls still waiting
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/scheduled-calls?status=scheduled"))
            .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
// List the calls still waiting
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/scheduled-calls?status=scheduled")
{
};
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
// List the calls still waiting
$curl = curl_init("https://api.aigently.ai/v1/scheduled-calls?status=scheduled");
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";
```

Cancel one before its time:

**curl**

```sh
#!/bin/sh
# Cancel a scheduled call. Only before its time: once placed, it is a conversation, which you end instead.
curl -sS --fail-with-body -X DELETE "https://api.aigently.ai/v1/scheduled-calls/4b6d8f0a-2c4e-4a6c-9e8a-0c2e4a6c8e0b" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Cancel a scheduled call. Only before its time: once placed, it is a conversation, which you end instead.
const response = await fetch("https://api.aigently.ai/v1/scheduled-calls/4b6d8f0a-2c4e-4a6c-9e8a-0c2e4a6c8e0b", {
  method: "DELETE",
  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
# Cancel a scheduled call. Only before its time: once placed, it is a conversation, which you end instead.
# pip install httpx
import os

import httpx

response = httpx.delete(
    "https://api.aigently.ai/v1/scheduled-calls/4b6d8f0a-2c4e-4a6c-9e8a-0c2e4a6c8e0b",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Cancel a scheduled call. Only before its time: once placed, it is a conversation, which you end instead.
package main

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

func main() {
	request, err := http.NewRequest("DELETE", "https://api.aigently.ai/v1/scheduled-calls/4b6d8f0a-2c4e-4a6c-9e8a-0c2e4a6c8e0b", 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
// Cancel a scheduled call. Only before its time: once placed, it is a conversation, which you end instead.
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/scheduled-calls/4b6d8f0a-2c4e-4a6c-9e8a-0c2e4a6c8e0b"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("DELETE", 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
// Cancel a scheduled call. Only before its time: once placed, it is a conversation, which you end instead.
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.Delete, "https://api.aigently.ai/v1/scheduled-calls/4b6d8f0a-2c4e-4a6c-9e8a-0c2e4a6c8e0b")
{
};
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
// Cancel a scheduled call. Only before its time: once placed, it is a conversation, which you end instead.
$curl = curl_init("https://api.aigently.ai/v1/scheduled-calls/4b6d8f0a-2c4e-4a6c-9e8a-0c2e4a6c8e0b");
curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => "DELETE",
    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";
```

Once its time has come it can no longer be cancelled. A call being placed right now answers
`scheduled_call_placing`, and a call already placed is a conversation, which you
[end](#end-it-yourself) instead.

A test key's scheduled calls become test calls that ring nobody, and only test keys see them. A
scheduled call is kept for 30 days after it is placed, cancelled or fails; the conversation it made
is kept like any other.

## Calls refused before they start

| Code | Why |
|---|---|
| `agent_not_outbound` | The agent answers calls; it does not place them. |
| `agent_not_published` | Publish the agent first. |
| `invalid_variables` | A value is missing or does not fit — `errors` names each. |
| `destination_not_allowed` | A number this deployment does not call. |
| `do_not_call` | The number is on your organization's [do-not-call list](#numbers-that-must-not-be-called). |
| `caller_id_unavailable` | The agent has no number it may call from. |
| `concurrency_limit_reached` | The organization already has as many calls going as it may. |
| `daily_limit_reached` | The organization has placed today's calls. |
| `test_number_required` | A test key calls only [test numbers](/guides/test-mode#test-numbers). |
| `outside_call_window` | The call's [window](#calling-hours) is closed. `detail` says when it opens. |
| `feature_disabled` | This deployment has phone calls switched off. A test key's calls are not affected. |

## Numbers that must not be called

Your organization keeps one do-not-call list, and no agent rings a number on it: a call through the
API, a campaign's contact, an appointment reminder, a transfer to a person or a test dial from the
console is refused before anything is dialled, with `do_not_call`. Keep it from your own systems — when somebody opts out in
your CRM, add them:

**curl**

```sh
#!/bin/sh
# Add a number to the do-not-call list. With a live key: the list decides which real people are called.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/do-not-call" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY" \
  --json '{
  "number": "+12025550188",
  "reason": "Asked not to be called again"
}'
```

**JavaScript**

```js
// Add a number to the do-not-call list. With a live key: the list decides which real people are called.
const response = await fetch("https://api.aigently.ai/v1/do-not-call", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "number": "+12025550188",
    "reason": "Asked not to be called again"
  }),
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Add a number to the do-not-call list. With a live key: the list decides which real people are called.
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://api.aigently.ai/v1/do-not-call",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
    json={
        "number": "+12025550188",
        "reason": "Asked not to be called again",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Add a number to the do-not-call list. With a live key: the list decides which real people are called.
package main

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

func main() {
	payload := []byte(`{
  "number": "+12025550188",
  "reason": "Asked not to be called again"
}`)
	request, err := http.NewRequest("POST", "https://api.aigently.ai/v1/do-not-call", 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
// Add a number to the do-not-call list. With a live key: the list decides which real people are called.
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 = """
            {
              "number": "+12025550188",
              "reason": "Asked not to be called again"
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/do-not-call"))
            .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
// Add a number to the do-not-call list. With a live key: the list decides which real people are called.
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/do-not-call")
{
    Content = new StringContent(
        """
        {
          "number": "+12025550188",
          "reason": "Asked not to be called again"
        }
        """,
        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
// Add a number to the do-not-call list. With a live key: the list decides which real people are called.
$curl = curl_init("https://api.aigently.ai/v1/do-not-call");
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([
        "number" => "+12025550188",
        "reason" => "Asked not to be called again",
    ]),
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

This needs `do_not_call:write` and a live key: the list decides which real people are called, so a
test key may read it but not change it. The list belongs to the whole organization, so reading or
changing it also takes a key that reaches every project. Numbers are matched by their digits, so `+1 (202) 555-0100`
and `12025550100` are one entry, and adding a listed number again answers `200` with the entry —
taking the new `reason`, if you sent one.

A number listed without its country code — `0798 798 906`, `(202) 555-0100` — also stops a call
that dials it with one, and the other way round: the national forms are worked out from the number
and from the line calling it. That can now and then refuse a number in another country that ends the
same way. The refusal's `detail` names the entry, so you can check it.

Ask whether a number is listed with `do_not_call:read`:

**curl**

```sh
#!/bin/sh
# Ask whether a number may be called. An empty list means it is not on it.
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/do-not-call?number=+12025550188" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Ask whether a number may be called. An empty list means it is not on it.
const response = await fetch("https://api.aigently.ai/v1/do-not-call?number=+12025550188", {
  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
# Ask whether a number may be called. An empty list means it is not on it.
# pip install httpx
import os

import httpx

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

**Go**

```go
// Ask whether a number may be called. An empty list means it is not on it.
package main

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

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/do-not-call?number=+12025550188", 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
// Ask whether a number may be called. An empty list means it is not on it.
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/do-not-call?number=+12025550188"))
            .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
// Ask whether a number may be called. An empty list means it is not on it.
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/do-not-call?number=+12025550188")
{
};
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
// Ask whether a number may be called. An empty list means it is not on it.
$curl = curl_init("https://api.aigently.ai/v1/do-not-call?number=+12025550188");
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";
```

Taking a number off lets it be called again, and is recorded in your organization's audit log under
the key:

**curl**

```sh
#!/bin/sh
# Take a number off the do-not-call list. With a live key. Recorded in the audit log under it.
curl -sS --fail-with-body -X DELETE "https://api.aigently.ai/v1/do-not-call/+12025550188" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Take a number off the do-not-call list. With a live key. Recorded in the audit log under it.
const response = await fetch("https://api.aigently.ai/v1/do-not-call/+12025550188", {
  method: "DELETE",
  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
# Take a number off the do-not-call list. With a live key. Recorded in the audit log under it.
# pip install httpx
import os

import httpx

response = httpx.delete(
    "https://api.aigently.ai/v1/do-not-call/+12025550188",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Take a number off the do-not-call list. With a live key. Recorded in the audit log under it.
package main

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

func main() {
	request, err := http.NewRequest("DELETE", "https://api.aigently.ai/v1/do-not-call/+12025550188", 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
// Take a number off the do-not-call list. With a live key. Recorded in the audit log under it.
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/do-not-call/+12025550188"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("DELETE", 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
// Take a number off the do-not-call list. With a live key. Recorded in the audit log under it.
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.Delete, "https://api.aigently.ai/v1/do-not-call/+12025550188")
{
};
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
// Take a number off the do-not-call list. With a live key. Recorded in the audit log under it.
$curl = curl_init("https://api.aigently.ai/v1/do-not-call/+12025550188");
curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => "DELETE",
    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";
```

A test key's own calls never ring anybody, so the list does not apply to them.
