# Outbound Calls

The Outbound Calls API allows you to programmatically initiate phone calls from your AI agents to any phone number.

**Base URL:** `https://api.krosai.com/v1/outbound-calls`

## Initiate Outbound Call

Start a new outbound call from your KrosAI phone number to a destination.

```
POST /outbound-calls
```

### Request Body

| Field          | Type    | Required | Description                                |
| -------------- | ------- | -------- | ------------------------------------------ |
| `from_number`  | string  | Yes      | Your KrosAI phone number (E.164 format)    |
| `to_number`    | string  | Yes      | Destination phone number (E.164 format)    |
| `endpoint_id`  | string  | Yes      | The endpoint (AI agent) to handle the call |
| `metadata`     | object  | No       | Custom metadata to attach to the call      |
| `webhook_url`  | string  | No       | Override webhook URL for this call         |
| `max_duration` | integer | No       | Maximum call duration in seconds           |

### Request

{% code title="curl" %}

```bash
curl -X POST "https://api.krosai.com/v1/outbound-calls" \
  -H "x-api-key: kros_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "from_number": "+2348012345678",
    "to_number": "+14155551234",
    "endpoint_id": "ep_abc123",
    "metadata": {
      "customer_id": "cust_xyz",
      "campaign": "follow-up-q1"
    }
  }'
```

{% endcode %}

### Response

{% code title="response.json" %}

```json
{
  "call_id": "call_xyz789",
  "status": "initiated",
  "from_number": "+2348012345678",
  "to_number": "+14155551234",
  "endpoint_id": "ep_abc123",
  "created_at": "2025-01-10T12:00:00Z"
}
```

{% endcode %}

## Phone Number Format

All phone numbers must be in **E.164 format**:

| Format    | Example          | Valid |
| --------- | ---------------- | ----- |
| E.164     | `+14155551234`   | ✅     |
| E.164     | `+2348012345678` | ✅     |
| Local     | `08012345678`    | ❌     |
| Formatted | `(415) 555-1234` | ❌     |

{% hint style="info" %}
The `from_number` must be a phone number owned by your organization.
{% endhint %}

## Call Lifecycle

When you initiate an outbound call, it goes through these stages:

initiated → ringing → answered → in\_progress → completed ↓ ↓ failed failed/no\_answer

### Status Values

| Status        | Description                       |
| ------------- | --------------------------------- |
| `initiated`   | Call request accepted, setting up |
| `ringing`     | Destination phone is ringing      |
| `answered`    | Destination answered              |
| `in_progress` | Call is active with AI agent      |
| `completed`   | Call ended normally               |
| `failed`      | Call failed to connect            |
| `no_answer`   | Destination didn't answer         |
| `busy`        | Destination was busy              |

## Metadata

Attach custom metadata to calls for tracking and analytics:

{% code title="metadata.json" %}

```json
{
  "from_number": "+2348012345678",
  "to_number": "+14155551234",
  "endpoint_id": "ep_abc123",
  "metadata": {
    "customer_id": "cust_123",
    "campaign_id": "camp_456",
    "lead_source": "website",
    "custom_field": "any value"
  }
}
```

{% endcode %}

Metadata is:

* Included in webhook payloads
* Searchable in call logs
* Available in analytics exports

## Maximum Duration

Set a maximum call duration to prevent unexpectedly long calls:

{% code title="max\_duration.json" %}

```json
{
  "from_number": "+2348012345678",
  "to_number": "+14155551234",
  "endpoint_id": "ep_abc123",
  "max_duration": 600
}
```

{% endcode %}

When the limit is reached, the call is automatically terminated.

## Error Responses

| Status | Code                   | Description                             |
| ------ | ---------------------- | --------------------------------------- |
| 400    | `INVALID_PHONE_NUMBER` | Invalid E.164 format                    |
| 400    | `NUMBER_NOT_OWNED`     | from\_number not in your inventory      |
| 400    | `OUTBOUND_DISABLED`    | Outbound calls disabled for this number |
| 400    | `INSUFFICIENT_BALANCE` | Not enough credits                      |
| 404    | `ENDPOINT_NOT_FOUND`   | Invalid endpoint\_id                    |
| 429    | `RATE_LIMIT_EXCEEDED`  | Too many concurrent calls               |

### Error Response Format

{% code title="error.json" %}

```json
{
  "error": "Phone number format is invalid. Use E.164 format (+14155551234)",
  "code": "INVALID_PHONE_NUMBER",
  "details": {
    "field": "to_number",
    "value": "4155551234"
  }
}
```

{% endcode %}

## Concurrent Call Limits

| Plan       | Concurrent Outbound Calls |
| ---------- | ------------------------- |
| Free       | 1                         |
| Pro        | 10                        |
| Business   | 50                        |
| Enterprise | Unlimited                 |

## Code Examples

{% tabs %}
{% tab title="TypeScript" %}
{% code title="outbound.ts" %}

```typescript
interface OutboundCallOptions {
  fromNumber: string;
  toNumber: string;
  endpointId: string;
  metadata?: Record<string, string>;
  maxDuration?: number;
}

async function initiateOutboundCall(options: OutboundCallOptions) {
  const response = await fetch('https://api.krosai.com/v1/outbound-calls', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.KROSAI_API_KEY!,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from_number: options.fromNumber,
      to_number: options.toNumber,
      endpoint_id: options.endpointId,
      metadata: options.metadata,
      max_duration: options.maxDuration,
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Call failed: ${error.code} - ${error.error}`);
  }

  return response.json();
}

// Usage
const call = await initiateOutboundCall({
  fromNumber: '+2348012345678',
  toNumber: '+14155551234',
  endpointId: 'ep_abc123',
  metadata: {
    customer_id: 'cust_123',
    campaign: 'welcome-series',
  },
  maxDuration: 300, // 5 minutes max
});

console.log(`Call initiated: ${call.call_id}`);
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="outbound.py" %}

```python
import requests
import os

def initiate_outbound_call(
    from_number: str,
    to_number: str,
    endpoint_id: str,
    metadata: dict = None,
    max_duration: int = None
):
    payload = {
        'from_number': from_number,
        'to_number': to_number,
        'endpoint_id': endpoint_id
    }
    
    if metadata:
        payload['metadata'] = metadata
    if max_duration:
        payload['max_duration'] = max_duration
    
    response = requests.post(
        'https://api.krosai.com/v1/outbound-calls',
        headers={
            'x-api-key': os.environ['KROSAI_API_KEY'],
            'Content-Type': 'application/json'
        },
        json=payload
    )
    
    if not response.ok:
        error = response.json()
        raise Exception(f"Call failed: {error['code']} - {error['error']}")
    
    return response.json()

# Usage
call = initiate_outbound_call(
    from_number='+2348012345678',
    to_number='+14155551234',
    endpoint_id='ep_abc123',
    metadata={
        'customer_id': 'cust_123',
        'campaign': 'welcome-series'
    }
)

print(f"Call initiated: {call['call_id']}")
```

{% endcode %}
{% endtab %}

{% tab title="Batch (TypeScript)" %}
{% code title="batch.ts" %}

```typescript
interface Contact {
  phone: string;
  customerId: string;
}

async function runCampaign(
  contacts: Contact[],
  fromNumber: string,
  endpointId: string
) {
  const results = [];
  
  for (const contact of contacts) {
    try {
      const call = await initiateOutboundCall({
        fromNumber,
        toNumber: contact.phone,
        endpointId,
        metadata: {
          customer_id: contact.customerId,
          campaign: 'batch-outreach',
        },
      });
      
      results.push({ success: true, callId: call.call_id, contact });
      
      // Respect rate limits - wait between calls
      await new Promise(r => setTimeout(r, 1000));
      
    } catch (error) {
      results.push({ success: false, error: error.message, contact });
    }
  }
  
  return results;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Webhook Integration

Receive real-time updates about your outbound calls:

{% code title="webhook-event.json" %}

```json
{
  "event": "call.completed",
  "call_id": "call_xyz789",
  "direction": "outbound",
  "from_number": "+2348012345678",
  "to_number": "+14155551234",
  "duration": 180,
  "cost_cents": 12,
  "metadata": {
    "customer_id": "cust_123"
  }
}
```

{% endcode %}

→ [Set up Webhooks](file:///webhooks/overview)

## Best Practices

### Do's ✅

* Validate phone numbers before calling
* Use metadata to track campaign performance
* Set max\_duration to prevent runaway calls
* Handle errors gracefully
* Respect rate limits in batch operations
* Test with small batches first

### Don'ts ❌

* Don't hardcode phone numbers in source code
* Don't exceed concurrent limits — queue calls instead
* Don't ignore webhook failures — implement retries
* Don't call without consent — follow local regulations

## Compliance Notes

When making outbound calls, ensure compliance with:

* TCPA (US) - Prior consent required
* GDPR (EU) - Data protection requirements
* Local regulations - Check destination country laws

KrosAI provides the infrastructure; you're responsible for compliance.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.krosai.com/voice/outbound-calls.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
