# 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.
