---
title: Implementing your HTTPS event endpoint for real-time call events
slug: voice-apis/implementing-your-https-event-endpoint-for-real-time-call-events
createdAt: 2026-09-17T07:17:46.983Z
---

## Overview

ExoML for Programmable Voice delivers **live call events** to your application so you can decide the next action while the caller is on the line.

To receive those events, host a public **HTTPS event endpoint**. Exotel sends each event as an HTTP POST with a JSON body and waits for your acknowledgement. Your application should:

1. Return an HTTP **2xx** status as soon as the event is accepted
2. Decide the next step in your application logic
3. Issue the next call command using **ExoML APIs** (play, gather, dial, bridge, hangup, and so on)

The HTTP response acknowledges receipt of the event. It does not return the next call verbs — those are always sent through ExoML APIs after the event.

:::BlockQuote
Live call / leg / bridge
&#x20;       │
&#x20;       ▼
Exotel ExoML Platform
&#x20;       │
&#x20;       │  POST application/json (HTTPS)
&#x20;       │  waits for HTTP 2xx ack
&#x20;       ▼
Your HTTPS event endpoint
&#x20;       │
&#x20;       │  1) Return 2xx
&#x20;       │  2) Decide next step
&#x20;       ▼
ExoML APIs (dial, play, gather, bridge, hangup, …)
&#x20;       │
&#x20;       ▼
Exotel executes action → sends next event
:::

Exotel acts as the HTTP client. You host the HTTPS server.

## Configuration steps

### 1. Implement your HTTPS event endpoint

Your service should:

- Accept POST requests with Content-Type: application/json
- Parse the JSON body described in Payload fields
- Return any HTTP **2xx** status once the event is accepted (a response body is not required; Exotel treats the status as the ack)

### 2. Deploy a public HTTPS URL

- Use a publicly reachable URL (any path), for example https\://voice.example.com/exotel/events
- Use a valid public TLS certificate in production
- Keep the handler low-latency — this endpoint sits on the live call path. Prefer completing heavy CRM or LLM work after you acknowledge the event.

### 3. Register the endpoint

**Outbound / API-driven calls**

When creating a resource, pass your HTTPS URL in:

| **API field**                           | **Example**                              |
| --------------------------------------- | ---------------------------------------- |
| Create Leg — leg\_event\_endpoint       | https\://voice.example.com/exotel/events |
| Create Bridge — bridge\_event\_endpoint | https\://voice.example.com/exotel/events |

Exotel delivers live events over HTTPS when the URL scheme is https\://.

**Inbound Programmable Voice (Exophone)**

Share your HTTPS event URL with Exotel Support or your Exotel contact so it can be attached to your Exophone for inbound live events.

### 4. (Optional) Enable Basic Auth

If you would like Exotel to authenticate when calling your endpoint, see Authentication.

## HTTP contract

| **Item**         | **Value**                                                                                                                                                          |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Method           | POST                                                                                                                                                               |
| Content-Type     | application/json                                                                                                                                                   |
| TLS              | HTTPS is required in production                                                                                                                                    |
| Success ack      | Any HTTP 2xx (response body is ignored)                                                                                                                            |
| Retries          | Up to **3 attempts** total for 408, 429, 5xx, timeouts, and connection errors. Other 4xx responses are not retried. After the final attempt, the event is dropped. |
| Next call action | Issue via ExoML APIs after acknowledging the event                                                                                                                 |

### Request headers

| **Header**          | **Description**                                                              |
| ------------------- | ---------------------------------------------------------------------------- |
| Content-Type        | application/json                                                             |
| User-Agent          | Exotel-Hermix/\<version>                                                     |
| X-Exotel-Event-Sid  | Same value as the body field event\_sid                                      |
| X-Exotel-Request-Id | \<event\_sid>-\<attempt\_number> (attempt numbers 1–3, for example …-1, …-2) |
| Authorization       | Basic … when Basic Auth is configured on your account                        |

### Example request

:::BlockQuote
POST /exotel/events HTTP/1.1
Host: voice.example.com
Content-Type: application/json
User-Agent: Exotel-Hermix/1.0
X-Exotel-Event-Sid: 3IgzDXwxdn1ylY5cGVFvYxnHEZo00000
X-Exotel-Request-Id: 3IgzDXwxdn1ylY5cGVFvYxnHEZo00000-1
Authorization: Basic dXNlcjpwYXNz
:::

:::BlockQuote
\{
&#x20; "event\_sid": "3IgzDXwxdn1ylY5cGVFvYxnHEZo00000",
&#x20; "event\_type": "leg\_action\_event",
&#x20; "event\_name": "leg\_failed\_to\_create",
&#x20; "event\_timestamp": "2026-08-31T18:28:00.529Z",
&#x20; "event\_data": \{
&#x20;   "data": \{},
&#x20;   "error": \{
&#x20;     "error\_code": "1001",
&#x20;     "message": "Destination unreachable",
&#x20;     "description": "Carrier returned 480"
&#x20;   }
&#x20; },
&#x20; "data": \{
&#x20;   "leg\_sid": "3IgzDZXUHsK9k67utaeupEgVNaI00000",
&#x20;   "account\_sid": "exotel60",
&#x20;   "contact\_uri": "09163816621",
&#x20;   "exophone": "08030752400",
&#x20;   "state": "completed",
&#x20;   "terminal\_status": "failed",
&#x20;   "start\_time": "2026-08-31T18:28:00.312Z",
&#x20;   "end\_time": "2026-08-31T18:28:00.403Z",
&#x20;   "date\_created": "2026-08-31T18:28:00.339Z",
&#x20;   "date\_updated": "2026-08-31T18:28:00.403Z"
&#x20; }
}
:::

### Example ack

:::BlockQuote
HTTP/1.1 200 OK
:::

201, 204, and other 2xx statuses are also accepted.

## Capacity and rate limits

Exotel does **not** apply a fixed requests-per-minute cap on live event POSTs to your HTTPS endpoint. Event volume follows your concurrent calls (several events can be emitted per call).

Please size your endpoint for peak concurrent traffic and return 2xx promptly — Hermix waits up to about **1 second** per delivery attempt. Prefer acknowledging quickly and doing heavier work asynchronously.

We recommend **not** using HTTP 429 as backpressure on this path. Exotel treats 429 as retriable (up to 3 attempts), which can delay delivery and still drop the event after the final attempt.

Separately, when your application calls **ExoML APIs** (create leg, actions, and so on), the default account limit is **100 requests per minute**. Contact your Exotel account manager if you need a higher API limit.

## Payload fields

All JSON keys use **snake\_case**.

**Timestamps** use RFC 3339 UTC with millisecond precision, for example 2026-08-31T18:28:00.339Z. This applies to event\_timestamp and nested fields such as start\_time, end\_time, date\_created, date\_updated, answered\_time, and ringing\_time.

| **Field**                 | **Type** | **Notes**                                                                     |
| ------------------------- | -------- | ----------------------------------------------------------------------------- |
| event\_sid                | string   | Unique event id                                                               |
| event\_type               | string   | Coarse group (see below)                                                      |
| event\_name               | string   | Specific event (for example leg\_answered, gather\_success)                   |
| event\_timestamp          | string   | Time the event was generated                                                  |
| action\_sid               | string   | Present when an action produced the event; may be omitted when unset          |
| action\_custom\_parameter | string   | Echo of your custom parameter when set                                        |
| event\_data               | object   | Action or error details when applicable; may be omitted when unset            |
| data                      | object   | Call context for the event (for example leg\_sid, account\_sid, state fields) |

### event\_type values

| **event\_type**          | **Typical use**                                             |
| ------------------------ | ----------------------------------------------------------- |
| leg\_lifecycle\_event    | Call leg lifecycle (ringing, answered, terminated, …)       |
| leg\_action\_event       | Action outcomes (play, gather, dial, failed\_to\_create, …) |
| bridge\_lifecycle\_event | Bridge lifecycle                                            |
| bridge\_action\_event    | Bridge action outcomes                                      |

We recommend routing and logging primarily on event\_name, and using event\_type for coarse grouping.

### Example event names

These are illustrative and not exhaustive:

- **Call leg:** leg\_connecting, leg\_ringing, leg\_answered, leg\_terminated, leg\_failed\_to\_create, leg\_incoming
- **Action:** play\_started, play\_completed, recording\_started, recording\_available, gather\_success, dial\_success, …
- **Bridge:** bridge\_created, bridge\_terminal, leg\_joined\_bridge, leg\_left\_bridge

Exact fields inside data and event\_data depend on the event.

## Authentication

You can optionally require authentication so your service only accepts callbacks from Exotel.

### HTTP Basic Auth

When Basic Auth is configured on your account, Exotel includes the following header on every event POST:

:::BlockQuote
Authorization: Basic \<base64(username\:password)>
:::

**Example**

If your configured credentials are:

- Username: exotel\_user
- Password: exotel\_pass

Exotel sends:

:::BlockQuote
Authorization: Basic ZXhvdGVsX3VzZXI6ZXhvdGVsX3Bhc3M=
:::

Your server should:

1. Read the Authorization header
2. Decode the Basic Auth value
3. Validate the username and password
4. Reject the request if credentials are missing or invalid

**Note:** Custom headers such as x-exotel-token are not supported. Please validate the standard Authorization Basic Auth header.

### How to enable authentication

1. Decide on the username and password (or API key and token) your service will accept.
2. Share those credentials with Exotel Support or your Exotel contact so they can be configured on your account.
3. Ensure your endpoint validates the Authorization header on every callback.

Once configured, Exotel automatically attaches Basic Auth credentials to outbound HTTPS event requests for your account.

### When authentication is not configured

If Basic Auth credentials are not configured on your account, Exotel still delivers events to your public HTTPS endpoint, without an Authorization header. In that case, protect your endpoint using TLS and, optionally, IP allowlisting.

### IP allowlisting

If you restrict inbound traffic by IP, please allow the following Exotel callback source addresses:

:::BlockQuote
35.154.174.161
13.233.217.51
98.130.5.99
:::

