# Webhook Integration

Easy Calling can invoke **external HTTP endpoints** (“webhooks”) during the call lifecycle. These are used to integrate with external systems (e.g., ticketing, CRM, analytics, custom automations).

This document is intended for **external developers** integrating their services with Easy Calling.

## Overview

Easy Calling supports call event notifications to a webhook URL configured per Voice App:

* **Begin-call notification** (call started)
* **End-call notification** (call completed)

## Events

### Begin-call notification

**What it represents**

* A call has been created/answered by the system and is now active within the Easy Calling lifecycle.

**When it is sent**

* Near the beginning of call processing, shortly after the call record has been created.

**Configuration key**

* `BeginCallEventProcessingUrl`

### End-call notification

**What it represents**

* A call has ended (terminated/completed). The payload includes the final call state as known to Easy Calling at the time of sending.

**When it is sent**

* During termination processing after the call is considered ended.

**Configuration key**

* `EndCallEventProcessingUrl`

## Request format

* Method: `POST`
* Content-Type: `application/json`
* Body: a JSON object representing the call record.

### Delivery expectations

Because call processing can be distributed and events can be retried upstream of your service, your webhook handler should be:

* **Idempotent**: handle potential duplicates using a stable key such as `activeCallId` (and/or `correlationId`).
* **Fast**: respond with `2xx` quickly and enqueue work for asynchronous processing.
* **Tolerant**: ignore unknown fields and handle missing optional fields.

### Success / failure

* A `2xx` response is treated as success.
* A non-`2xx` response is treated as failure.

## Payload

The webhook payload is a JSON representation of the Easy Calling call record.

Notes:

* Field names and presence can vary by call type and configuration.
* Additional fields may be added over time; consumers should ignore unknown fields.
* The payload may contain personally identifiable information (PII). Handle it accordingly.

### CallType

`CallType` is an integer that describes the overall classification of the call. Treat this as an enum value:

Known values:

| Value | Meaning              |
| ----: | -------------------- |
|     0 | GenericCall          |
|     1 | AnsweredCall         |
|     2 | MissedCall           |
|     3 | InitialAnsweredCall  |
|     4 | ActiveCall           |
|     5 | VoiceMail            |
|     6 | CallTransfered       |
|     7 | CallAnsweredManually |
|     8 | OutboundCall         |

Forward-compatibility:

* Do not hard-fail on unknown `CallType` values; new values may be introduced.
* If you build routing/analytics based on `CallType`, prefer a safe default branch for unknown values.

### Example payload (dummy data)

This is a real-world payload structure filled with dummy data. Values like phone numbers, tenant IDs, app IDs, names, and emails are fictitious.

```json
{
  "CallType": 4,
  "CorrelationId": "7c7f9b2a-2e2b-4e7b-8a7f-3f4c6f4c1b2a",
  "TenantId": "11111111-2222-3333-4444-555555555555",
  "Caller": "+41791234567",
  "StartDateTime": "2026-01-19T15:30:08.4806311Z",
  "EndDateTime": "2026-01-19T15:30:38.4806311Z",
  "TimeZone": "W. Europe Standard Time",
  "TransferHistory": [],
  "LookupResult": {
    "IsSuccessfulResult": false,
    "Id": "+41791234567",
    "Name": "+41791234567",
    "Company": "",
    "Phone": "+41791234567",
    "MobilePhone": "",
    "Email": "",
    "Website": "",
    "Address": "",
    "Zip": "",
    "City": "",
    "State": "",
    "Country": ""
  },
  "VoiceApp": {
    "TenantId": "11111111-2222-3333-4444-555555555555",
    "VoiceAppId": "b8c2c7d1-6d0a-4a6c-bb7d-9ad2aa72c6ef",
    "VoiceAppType": 2,
    "VoiceAppName": "Demo Location West",
    "ApplicationId": "5d7b4f8c-1d03-4fd2-a1e8-6b7c9f9c2e10",
    "IsRealTime": true,
    "EnableNotes": true,
    "TeamsRoutingEnabled": false,
    "Description": "Demo Location West",
    "AvailableTags": [
      { "id": "c2c8f0a3-4e6f-4a3c-8b8f-5b0f6f8b7a12", "label": "Appointment" },
      { "id": "9fd2c1a4-7b1d-4d2a-9d10-9e1c2b3a4d5e", "label": "Payment" }
    ],
    "ShowAnsweredCalls": true,
    "ShowNotificationInChannel": false,
    "CallingConfiguration": {
      "MaxConcurrentCalls": 3,
      "IgnoreAgentAvailability": true,
      "ConsiderPresenceStatus": true,
      "WaitingTimeout": 60,
      "VoicemailEnabled": true,
      "AllowCallTransfer": true,
      "ToneConfigurationsEnabled": false,
      "ToneConfigurations": [],
      "RoutingConfiguration": {
        "Method": 0,
        "AgentResponseTimeoutSeconds": 60,
        "MaxAgentsToTryInSerial": 5
      },
      "Managers": [
        {
          "id": "3a8f0f2e-7d2a-4a9a-9c2b-0f1e2d3c4b5a",
          "displayName": "Alex Manager",
          "userPrincipalName": "alex.manager@example.invalid",
          "mail": "alex.manager@example.invalid",
          "jobTitle": "",
          "department": ""
        }
      ],
      "Agents": [
        {
          "id": "8d1d2c3b-4a5e-4c6d-8e7f-9012a3b4c5d6",
          "displayName": "Jamie Agent",
          "userPrincipalName": "jamie.agent@example.invalid",
          "mail": "jamie.agent@example.invalid",
          "jobTitle": "",
          "department": ""
        }
      ],
      "WaitingMusicUrl": "2ad2b3c4-d5e6-4f70-8a9b-1c2d3e4f5a6b",
      "VoicemailGreetingUrl": "6f5e4d3c-2b1a-4098-8f7e-6d5c4b3a2f1e",
      "ForwardBehavior": {
        "BehaviorType": 0,
        "TargetId": "",
        "TargetName": ""
      }
    },
    "TimeZone": "W. Europe Standard Time",
    "Language": "de-de",
    "TabPageId": "configureTab",
    "NotificationConfigurations": [],
    "_etag": "\"etag-demo-voiceapp\"",
    "_ts": 0,
    "id": "b8c2c7d1-6d0a-4a6c-bb7d-9ad2aa72c6ef",
    "type": "VoiceApp"
  },
  "HasBeenNotified": false,
  "Comment": "",
  "HandledByUserIds": [],
  "Tags": [],
  "Transcription": "",
  "State": 2,
  "ActiveCallId": "0d003480-d90e-479a-a4e0-99086a14f9a1",
  "ApplicationInstance": "b8c2c7d1-6d0a-4a6c-bb7d-9ad2aa72c6ef",
  "CallQueue": "",
  "Description": "",
  "ChannelId": "",
  "_etag": "\"etag-demo-easycall\"",
  "id": "7195b91d-8598-4a70-8d6d-c1ee4e732627",
  "type": "EasyCall",

  "_rid": "demoRid==",
  "_self": "dbs/demo==/colls/demo==/docs/demo==/",
  "_attachments": "attachments/",
  "_ts": 1768836611
}
```

Notes:

* Use `ActiveCallId` (and/or `CorrelationId`) for idempotency/deduplication.
* Fields starting with `_` are internal metadata and should be treated as optional.

## Security guidance

* Only accept requests over HTTPS.
* Validate the sender using an agreed mechanism (e.g., shared secret header, request signing, mTLS) based on your deployment.
* Treat payload data as sensitive and apply appropriate retention and access controls.


---

# 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.easycalling.easyplatform.app/developer/webhook-notifications.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.
