# Webhook Configuration

## Overview

HeyMilo Webhooks allow you to receive real-time HTTP notifications when key events occur during the candidate interview lifecycle. Instead of polling the API for updates, webhooks push data to your specified endpoint as soon as an event happens — enabling seamless automation, ATS syncing, and custom workflow triggers.

With webhooks, you can:

* Get notified instantly when a candidate starts or completes an interview
* Receive full interview reports as soon as they are generated
* Automate downstream actions like ATS updates, email notifications, or scoring pipelines
* Build event-driven integrations without continuous API polling

## How Webhooks Work

1. **Register a webhook** via the API, specifying a destination URL, the event type you want to subscribe to, and the posting (job) it applies to.
2. **HeyMilo fires an HTTP request** to your URL whenever the subscribed event occurs.
3. **Your endpoint receives the payload** containing all relevant event data (candidate info, interview details, scores, etc.).

```
┌──────────┐         Event occurs         ┌────────────────┐
│  HeyMilo │ ──── HTTP POST/GET ────────► │  Your Endpoint │
└──────────┘   (JSON payload)             └────────────────┘
```

***

## Authentication

Webhook API endpoints require authentication using the `X-API-KEY` header, the same key used for all Public API requests.

```http
X-API-KEY: your_api_key_here
```

***

## Webhook Event Types

HeyMilo supports the following webhook event types:

| Event Type            | Description                                                       |
| --------------------- | ----------------------------------------------------------------- |
| `interview_started`   | Triggered when a candidate begins an interview session            |
| `interview_completed` | Triggered when a candidate finishes all steps of an interview     |
| `report_available`    | Triggered when the AI-generated interview report is ready to view |

### Event Lifecycle

A typical candidate journey triggers events in this order:

```
interview_started  ──►  interview_completed  ──►  report_available
```

> **Note:** The `report_available` event fires after AI analysis is complete, which may occur several seconds to minutes after the interview is marked as completed.

***

## Webhook API Endpoints

All webhook endpoints use the base URL:

```
https://api.heymilo.network/api/webhook
```

### 1. Create Webhook

**POST** `/create`

Register a new webhook to receive event notifications for a specific job posting.

#### Request Body

```json
{
  "posting_id": "posting_123",
  "url": "https://your-domain.com/webhook-endpoint",
  "event_type": "interview_completed",
  "http_method": "post"
}
```

#### Request Schema

| Field         | Type         | Required | Description                                                                |
| ------------- | ------------ | -------- | -------------------------------------------------------------------------- |
| `posting_id`  | string       | Yes      | ID of the job posting this webhook applies to                              |
| `url`         | string (URL) | Yes      | The destination URL that will receive the webhook payload                  |
| `event_type`  | string       | Yes      | One of: `interview_started`, `interview_completed`, `report_available`     |
| `http_method` | string       | No       | HTTP method used to deliver the webhook: `get` or `post` (default: `post`) |

#### Example Request

```bash
curl -X POST "https://api.heymilo.network/api/webhook/create" \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "posting_id": "posting_123",
    "url": "https://your-domain.com/webhook-endpoint",
    "event_type": "interview_completed",
    "http_method": "post"
  }'
```

#### Response

```json
{
  "webhook_id": "webhook_789",
  "posting_id": "posting_123",
  "url": "https://your-domain.com/webhook-endpoint",
  "event_type": "interview_completed",
  "http_method": "post",
  "is_active": true,
  "created_at": 1703097600.0,
  "updated_at": 1703097600.0
}
```

#### Response Schema

| Field         | Type    | Description                                       |
| ------------- | ------- | ------------------------------------------------- |
| `webhook_id`  | string  | Unique identifier for the created webhook         |
| `posting_id`  | string  | The job posting this webhook is associated with   |
| `url`         | string  | Destination URL for webhook delivery              |
| `event_type`  | string  | The subscribed event type                         |
| `http_method` | string  | HTTP method used for delivery (`get` or `post`)   |
| `is_active`   | boolean | Whether the webhook is currently active           |
| `created_at`  | float   | Unix timestamp when the webhook was created       |
| `updated_at`  | float   | Unix timestamp when the webhook was last modified |

***

### 2. Get Webhook

**GET** `/fetch/{webhook_id}`

Retrieve a specific webhook configuration by its ID.

#### Path Parameters

| Parameter    | Type   | Required | Description                  |
| ------------ | ------ | -------- | ---------------------------- |
| `webhook_id` | string | Yes      | The unique ID of the webhook |

#### Example Request

```bash
curl -X GET "https://api.heymilo.network/api/webhook/fetch/webhook_789" \
  -H "X-API-KEY: your_api_key_here"
```

#### Response

```json
{
  "webhook_id": "webhook_789",
  "posting_id": "posting_123",
  "url": "https://your-domain.com/webhook-endpoint",
  "event_type": "interview_completed",
  "http_method": "post",
  "is_active": true,
  "created_at": 1703097600.0,
  "updated_at": 1703097600.0
}
```

***

### 3. Get All Webhooks

**GET** `/all`

Retrieve all webhooks for your workspace, with optional filters.

#### Query Parameters

| Parameter    | Type   | Required | Description                                                                          |
| ------------ | ------ | -------- | ------------------------------------------------------------------------------------ |
| `posting_id` | string | No       | Filter webhooks by job posting ID                                                    |
| `event_type` | string | No       | Filter by event type: `interview_started`, `interview_completed`, `report_available` |

#### Example Request

```bash
curl -X GET "https://api.heymilo.network/api/webhook/all?posting_id=posting_123&event_type=interview_completed" \
  -H "X-API-KEY: your_api_key_here"
```

#### Response

```json
[
  {
    "webhook_id": "webhook_789",
    "posting_id": "posting_123",
    "url": "https://your-domain.com/webhook-endpoint",
    "event_type": "interview_completed",
    "http_method": "post",
    "is_active": true,
    "created_at": 1703097600.0,
    "updated_at": 1703097600.0
  },
  {
    "webhook_id": "webhook_790",
    "posting_id": "posting_123",
    "url": "https://your-domain.com/report-endpoint",
    "event_type": "report_available",
    "http_method": "post",
    "is_active": true,
    "created_at": 1703097700.0,
    "updated_at": 1703097700.0
  }
]
```

***

### 4. Deactivate Webhook

**POST** `/deactivate/{webhook_id}`

Deactivate an existing webhook. Deactivated webhooks will no longer receive event notifications.

#### Path Parameters

| Parameter    | Type   | Required | Description                  |
| ------------ | ------ | -------- | ---------------------------- |
| `webhook_id` | string | Yes      | The unique ID of the webhook |

#### Example Request

```bash
curl -X POST "https://api.heymilo.network/api/webhook/deactivate/webhook_789" \
  -H "X-API-KEY: your_api_key_here"
```

***

## Webhook Payload Schemas

When an event fires, HeyMilo sends an HTTP request to your registered URL with a JSON payload. The payload structure varies by event type. Each event type has a distinct schema — see the full payloads and field-by-field breakdowns below.

***

### Payload: `interview_started`

Sent when a candidate begins an interview session.

```json
{
  "interview_id": "A1B2C3D4E5F60718",
  "posting_id": "F9E8D7C6",
  "timestamp": 1770816760.310334,
  "metadata": {
    "candidate_name": "Jane Doe",
    "candidate_email": "jane.doe@example.com",
    "candidate_id": "B4C5D6E7",
    "metadata": {
      "source": "user_ingestion",
      "job_id": "F9E8D7C6"
    }
  }
}
```

#### `interview_started` Payload Schema

| Field          | Type   | Description                               |
| -------------- | ------ | ----------------------------------------- |
| `interview_id` | string | Unique identifier for the interview       |
| `posting_id`   | string | The job posting ID                        |
| `timestamp`    | float  | Unix timestamp when the interview started |
| `metadata`     | object | Candidate metadata object (see below)     |

**Candidate Metadata Object:**

| Field             | Type        | Description                                                              |
| ----------------- | ----------- | ------------------------------------------------------------------------ |
| `candidate_name`  | string      | Candidate's full name                                                    |
| `candidate_email` | string      | Candidate's email address                                                |
| `candidate_id`    | string      | Unique identifier for the candidate                                      |
| `metadata`        | object/null | Additional ingestion metadata (can be `null`). See inner metadata below. |

**Inner Metadata Object** *(nullable)*:

| Field    | Type   | Description                                             |
| -------- | ------ | ------------------------------------------------------- |
| `source` | string | How the candidate was ingested (e.g., `user_ingestion`) |
| `job_id` | string | The job/posting ID associated with the ingestion        |

> **Note:** The inner `metadata` object can be `null` if no additional ingestion context is available.

***

### Payload: `interview_completed`

Sent when a candidate finishes all required steps of an interview.

```json
{
  "interview_id": "A1B2C3D4E5F60718",
  "agent_conversation_id": "",
  "parent_conversation_id": "7A8B9C0D1E2F3A4B",
  "engagement_id": "",
  "posting_id": "F9E8D7C6",
  "timestamp": 1770810397.0832531,
  "workflow_completed": true,
  "completion_timestamp": 1770810397.0827055
}
```

#### `interview_completed` Payload Schema

| Field                    | Type    | Description                                                                |
| ------------------------ | ------- | -------------------------------------------------------------------------- |
| `interview_id`           | string  | Unique identifier for the interview                                        |
| `agent_conversation_id`  | string  | Conversation ID for the agent interaction (empty string if not applicable) |
| `parent_conversation_id` | string  | Parent conversation ID linking related interview sessions                  |
| `engagement_id`          | string  | Engagement tracking ID (empty string if not applicable)                    |
| `posting_id`             | string  | The job posting ID                                                         |
| `timestamp`              | float   | Unix timestamp when the event was emitted                                  |
| `workflow_completed`     | boolean | Whether the full agentic workflow has been completed                       |
| `completion_timestamp`   | float   | Unix timestamp when the interview was actually completed                   |

***

### Payload: `report_available`

Sent when the AI-generated interview report is ready. This is the most comprehensive payload, containing the full analysis data: scores, highlights, scorecard, communication evaluation, transcript, interview data model, company info, and posting info.

```json
{
  "interview_id": "A1B2C3D4E5F60718",
  "posting_id": "F9E8D7C6",
  "metadata": {
    "candidate_name": "Jane Doe",
    "candidate_email": "jane.doe@example.com",
    "candidate_id": "B4C5D6E7F8091A2B",
    "metadata": {
      "candidate_metadata_id": "550e8400-e29b-41d4-a716-446655440000",
      "resume_url": "https://example.cdn.heymilo.ai/WORKSPACE123/source/Jane_Doe_12345678.pdf"
    }
  },
  "report_url": "https://cdn.heymilo.ai/reports/MOCK_REPORT_HASH/jane-doe-2026-02-11.pdf",
  "timestamp": 1770817943.3959253,
  "interview_type": "web_interview",
  "agentic_data": {
    "web_interview_info": {
      "summary": {
        "interview_id": "A1B2C3D4E5F60718",
        "highlights": [
          "Demonstrated strong understanding of core technical concepts.",
          "Provided clear examples of past project experience with measurable outcomes.",
          "Communicated ideas concisely and showed good problem-solving approach."
        ],
        "lowlights": [
          "Did not elaborate on experience with distributed systems.",
          "Missed opportunity to connect leadership experience to the role requirements."
        ],
        "match_score": 78
      },
      "scorecard": {
        "interview_id": "A1B2C3D4E5F60718",
        "match_details": [
          "Demonstrated strong understanding of core technical concepts.",
          "Provided clear examples of past project experience with measurable outcomes.",
          "Did not elaborate on experience with distributed systems.",
          "Missed opportunity to connect leadership experience to the role requirements.",
          "Communicated ideas concisely and showed good problem-solving approach."
        ],
        "questions": [
          {
            "question_id": "Q1A2B3C4",
            "question": "Can you walk me through your professional background and key achievements in your recent roles?",
            "evaluation_summary": "Provided a clear career overview.\nMentioned specific companies and roles.\nDid not fully connect experience to this position.\nMissing some quantitative achievements.",
            "evaluation_criteria": "A good answer provides a clear, concise summary of relevant work experience, highlighting key roles, responsibilities, and achievements aligned with the position.",
            "preview_timestamp": 28,
            "evaluation_score": 7,
            "rank": 1,
            "chat_transcript": [
              {
                "speaker": "Candidate",
                "text": "Sure, I started my career at ...",
                "timestamp": 28,
                "timestamp_str": "00:28"
              }
            ],
            "score_of_1": "Very vague, unclear, or incomplete overview. Missing company names, roles, or timelines. No achievements or impact mentioned.",
            "score_of_5": "Gives a clear, chronological flow of career steps. Mentions companies and role titles. Provides relevant achievements and connects experience to the role.",
            "score_weight": 5,
            "not_scored": false
          },
          {
            "question_id": "Q5D6E7F8",
            "question": "Describe a challenging project you led and how you handled obstacles along the way.",
            "evaluation_summary": "Provided general insights on project challenges.\nArticulated team coordination well.\nCould have gone deeper on measurable impact.",
            "evaluation_criteria": "A good answer describes a specific project, the obstacles faced, actions taken, and outcomes achieved with clarity and structure.",
            "preview_timestamp": 274,
            "evaluation_score": 6,
            "rank": 2,
            "chat_transcript": [
              {
                "speaker": "Candidate",
                "text": "One of the most challenging projects was when ...",
                "timestamp": 274,
                "timestamp_str": "04:34"
              },
              {
                "speaker": "Candidate",
                "text": "We ended up restructuring the approach by ...",
                "timestamp": 418,
                "timestamp_str": "06:58"
              }
            ],
            "score_of_1": "A weak answer is vague, lacks specific details, and fails to describe actions taken or outcomes achieved.",
            "score_of_5": "A strong answer clearly describes the project, obstacles, actions, and outcomes with specific details and measurable results.",
            "score_weight": 5,
            "not_scored": false
          }
        ],
        "tags": [
          "Technical Skills",
          "Project Leadership",
          "Problem Solving"
        ]
      },
      "communication": {
        "interview_id": "A1B2C3D4E5F60718",
        "speech_score": -1,
        "evaluations": [],
        "template_id": null
      },
      "transcript": {
        "interview_id": "A1B2C3D4E5F60718",
        "transcript": [
          {
            "speaker": "bot",
            "text": "Hi, Jane Doe. Thanks for taking the time to meet with me. My name is Sarah, and I'm the AI interviewer. How is your day going so far?",
            "timestamp": 0,
            "timestamp_str": "00:00"
          },
          {
            "speaker": "user",
            "text": "Thank you. Have a nice day.",
            "timestamp": 827,
            "timestamp_str": "13:47"
          },
          {
            "speaker": "bot",
            "text": "Thank you for your time, Jane Doe. I appreciate you completing the interview.",
            "timestamp": 830,
            "timestamp_str": "13:50"
          }
        ]
      },
      "audio_recording_link": "https://cdn.heymilo.ai/audio/merged/1770817889-MOCK01/tmp_audio.wav",
      "video_recording_link": "https://cdn.heymilo.ai/videos/merged/1770817901-MOCK02/tmp_video.mp4"
    }
  },
  "interview_data_model": {
    "interview_id": "A1B2C3D4E5F60718",
    "details": {
      "interview_id": "A1B2C3D4E5F60718",
      "candidate_id": "B4C5D6E7F8091A2B",
      "interviewed_on": 1770817918.4658782,
      "score": 0,
      "name": "Jane Doe",
      "email": "jane.doe@example.com",
      "actions": null,
      "status": null,
      "workflow_status": {
        "interview_id": "A1B2C3D4E5F60718",
        "step_by_step": [
          {
            "complete": true,
            "required": false,
            "order": 1,
            "id": "web_interview",
            "started": true,
            "details": {
              "details": "Raw Interview Data Has Been Processed",
              "title": "Interview Processed Successfully",
              "description": "Candidate has attended the interview and completed it"
            },
            "raw_status": "INTERVIEW_PROCESSED_RAW_FILES",
            "last_updated_timestamp": 1770817918.4658782,
            "knocked_out": false,
            "completion_percentage": 1,
            "completion_metadata": null,
            "is_analyzing_call": false,
            "total_duration": null,
            "state_flags": {
              "is_completed": true,
              "is_incomplete": false,
              "is_analyzing_call": false,
              "is_in_progress": false
            }
          }
        ],
        "all_complete": true,
        "last_engagement": 1770817918.4658782,
        "metadata": null
      },
      "metadata": {
        "resume": null,
        "web_interview": {
          "final_score": 78,
          "skill_highlights": [
            "Technical Skills",
            "Project Leadership",
            "Problem Solving"
          ],
          "audio_recording_link": "https://cdn.heymilo.ai/audio/merged/1770817889-MOCK01/tmp_audio.wav",
          "video_recording_link": "https://cdn.heymilo.ai/videos/merged/1770817901-MOCK02/tmp_video.mp4"
        },
        "sms": null,
        "form": null
      },
      "candidate_feedback": null,
      "candidate_next_steps": null,
      "candidate_interview_url": "https://gointerview.co/example-company/i/A1B2C3D4E5F60718"
    },
    "company_info": {
      "company_id": "COMP1234",
      "workspace_id": "WS5678",
      "created_at": 1758332488.5218983,
      "updated_at": null,
      "created_by": "email|abc123def456",
      "name": "Example Corp",
      "image_url": "https://heymilo-west-0.s3.us-west-2.amazonaws.com/airstrip/WS5678/example-logo.png",
      "intro_message": null,
      "company_background": null,
      "voice_id": null,
      "knowledge_base": null,
      "interview_domain": null,
      "interview_domain_id": null,
      "sender_email_id": null,
      "address": null,
      "address_data": null,
      "timezone": null,
      "base_web_interview_config": {
        "backdrop_image_url": null,
        "avatar_image_url": null,
        "remove_heymilo_branding": null,
        "intro_message": null,
        "outro_message": null,
        "welcome_back_message": null
      },
      "message_settings": null,
      "sms_agent_config": null,
      "pdf_generation_config": null,
      "cheat_detection": null,
      "allow_white_labeling": null,
      "disable_all_candidate_communications": null,
      "interview_completion_confidence_threshold": 0.7,
      "retention_policy_days": null,
      "video_cheat_detection_enabled": null,
      "form_agent_enabled": null,
      "calendar_management_enabled": null,
      "email_templates_enabled": null,
      "phone_calls_enabled": null,
      "profile_picture_processing": null,
      "chat_agent_enabled": null,
      "copilot_enabled": null,
      "design_templates_enabled": null,
      "interview_templates_enabled": null,
      "sourcing_configs_enabled": null,
      "pbac_enabled": null,
      "email_report_to_candidate_enabled": null
    },
    "posting_info": {
      "posting_id": "F9E8D7C6",
      "title": "Senior Software Engineer",
      "name": "abc-def-123-456-789",
      "internal_name": null,
      "description": "<p><strong>About the Role</strong></p><p>We are looking for a Senior Software Engineer to join our growing team...</p>",
      "root_level_configs": {
        "instructions": "Conduct a structured technical interview with the candidate.",
        "company_overview": "Example Corp is a technology company building innovative solutions.",
        "job_overview": null,
        "interview_process_overview": null,
        "company_overview_delivery_mode": "summarized_delivery",
        "job_overview_delivery_mode": "exact_delivery",
        "interview_process_overview_delivery_mode": "exact_delivery",
        "language": "en",
        "interviewer_name": "Sarah",
        "is_multilingual": false
      },
      "company_id": "COMP1234",
      "created_at": 1769595148.8779235,
      "last_updated": 1769597734.2408419,
      "archived": false,
      "deadline": 1833235200,
      "finalized": true,
      "test_posting": false,
      "agentic_workflow": [
        {
          "type": "web_interview",
          "config": {
            "instructions": "Conduct a structured technical interview focusing on backend development and system design.",
            "company_overview": "Example Corp builds innovative cloud-based solutions for enterprise clients.",
            "job_overview": "The role involves designing and implementing scalable backend services.",
            "interview_process_overview": null,
            "company_overview_delivery_mode": null,
            "job_overview_delivery_mode": null,
            "interview_process_overview_delivery_mode": null,
            "language": "en",
            "interviewer_name": "Sarah",
            "is_multilingual": false,
            "video": true,
            "phone_calls_enabled": false,
            "voice_id": "voice-1",
            "interview_type": "voice",
            "prompt_version": "v2",
            "web_interview_config": {
              "backdrop_image_url": "",
              "avatar_image_url": "https://heymilo-west-0.s3.us-west-2.amazonaws.com/airstrip/MOCK/avatar.png",
              "remove_heymilo_branding": null,
              "intro_message": "Hi {{name}}, thanks for taking the time to meet with me! My name is {{interviewer_name}}, and I'm the AI interviewer. How is your day going so far?",
              "outro_message": "Thank you for your time {{name}}, I appreciate you completing the interview!",
              "welcome_back_message": "",
              "min_duration_seconds": null,
              "max_duration_seconds": null,
              "show_transcript": true,
              "candidate_presence_enabled": true,
              "vapi_call_config": {
                "end_call_message": "",
                "idle_message_max_spoken_count": null,
                "idle_timeout_seconds": 10,
                "wait_seconds": 2.4,
                "backoff_seconds": 5,
                "llm_model": null,
                "llm_temperature": 0.7
              }
            },
            "min_evaluation_score": 0,
            "num_questions": 5,
            "cheat_detection_config": {
              "enabled": false,
              "threshold": 70,
              "types": []
            }
          },
          "unique_id": null
        }
      ],
      "step_workflow": {
        "workflow": {
          "workflow_id": "wf_F9E8D7C6",
          "posting_id": "F9E8D7C6",
          "created_at": 1769595148.8779235
        },
        "steps": [
          {
            "step_id": "step_001",
            "workflow_id": "wf_F9E8D7C6",
            "step_type": "web_interview",
            "rank": 1,
            "details": "",
            "created_at": 1769595148.8779235,
            "depends_on": null
          }
        ]
      },
      "phone_number_id": null,
      "sender_email_id": "",
      "email_template_group_id": null,
      "design_template_group_id": null,
      "sourcing_config_ids": [],
      "redirect_url": null,
      "scheduling_url": null,
      "event_slug": null,
      "cal_team_id": null,
      "team_slug": null,
      "mascot_url": "https://heymilo-west-0.s3.us-west-2.amazonaws.com/avatars/mascot/COMP1234/F9E8D7C6/milo_mock.png",
      "mascot_id": "MASCOT123456",
      "days_to_complete": null,
      "ats_metadata": null,
      "allow_sms_comms": false,
      "max_retakes": null,
      "retake_cooldown_days": null,
      "is_template": false,
      "email_report_to_candidate": false
    },
    "web_interview_info": {
      "summary": {
        "interview_id": "A1B2C3D4E5F60718",
        "highlights": [
          "Demonstrated strong understanding of core technical concepts.",
          "Provided clear examples of past project experience with measurable outcomes.",
          "Communicated ideas concisely and showed good problem-solving approach."
        ],
        "lowlights": [
          "Did not elaborate on experience with distributed systems.",
          "Missed opportunity to connect leadership experience to the role requirements."
        ],
        "match_score": 78
      },
      "scorecard": {
        "interview_id": "A1B2C3D4E5F60718",
        "match_details": [
          "Demonstrated strong understanding of core technical concepts.",
          "Provided clear examples of past project experience with measurable outcomes.",
          "Did not elaborate on experience with distributed systems.",
          "Missed opportunity to connect leadership experience to the role requirements.",
          "Communicated ideas concisely and showed good problem-solving approach."
        ],
        "questions": [
          {
            "question_id": "Q1A2B3C4",
            "question": "Can you walk me through your professional background and key achievements in your recent roles?",
            "evaluation_summary": "Provided a clear career overview.\nMentioned specific companies and roles.\nDid not fully connect experience to this position.\nMissing some quantitative achievements.",
            "evaluation_criteria": "A good answer provides a clear, concise summary of relevant work experience.",
            "preview_timestamp": 28,
            "evaluation_score": 7,
            "rank": 1,
            "chat_transcript": [
              {
                "speaker": "Candidate",
                "text": "Sure, I started my career at ...",
                "timestamp": 28,
                "timestamp_str": "00:28"
              }
            ],
            "score_of_1": "Very vague, unclear, or incomplete overview.",
            "score_of_5": "Gives a clear, chronological flow of career steps with relevant achievements.",
            "score_weight": 5,
            "not_scored": false,
            "extracted_text": null
          }
        ],
        "tags": [
          "Technical Skills",
          "Project Leadership",
          "Problem Solving"
        ]
      },
      "communication": {
        "interview_id": "A1B2C3D4E5F60718",
        "speech_score": -1,
        "evaluations": [],
        "template_id": null
      },
      "transcript": {
        "interview_id": "A1B2C3D4E5F60718",
        "transcript": [
          {
            "speaker": "user",
            "text": "Thank you. Have a nice day.",
            "timestamp": 827,
            "timestamp_str": "13:47"
          },
          {
            "speaker": "bot",
            "text": "Thank you for your time, Jane Doe. I appreciate you completing the interview.",
            "timestamp": 830,
            "timestamp_str": "13:50"
          }
        ]
      },
      "audio_recording_link": "https://cdn.heymilo.ai/audio/merged/1770817889-MOCK01/tmp_audio.wav",
      "video_recording_link": "https://cdn.heymilo.ai/videos/merged/1770817901-MOCK02/tmp_video.mp4"
    },
    "resume_interview_info": null,
    "sms_interview_info": null,
    "form_interview_info": null
  }
}
```

#### `report_available` Top-Level Schema

| Field                  | Type   | Description                                                          |
| ---------------------- | ------ | -------------------------------------------------------------------- |
| `interview_id`         | string | Unique identifier for the interview                                  |
| `posting_id`           | string | The job posting ID                                                   |
| `metadata`             | object | Candidate metadata (same structure as `interview_started`)           |
| `report_url`           | string | Direct URL to download the generated PDF report                      |
| `timestamp`            | float  | Unix timestamp when the report became available                      |
| `interview_type`       | string | Type of interview (e.g., `web_interview`)                            |
| `agentic_data`         | object | AI-generated analysis data (summary, scorecard, transcript, etc.)    |
| `interview_data_model` | object | Full interview record with candidate details, company & posting info |

***

#### `agentic_data` Object

| Field                | Type        | Description                                                           |
| -------------------- | ----------- | --------------------------------------------------------------------- |
| `web_interview_info` | object/null | AI analysis of web/voice interview (present if interview type is web) |

#### `agentic_data.web_interview_info` Object

| Field                  | Type   | Description                                                  |
| ---------------------- | ------ | ------------------------------------------------------------ |
| `summary`              | object | High-level interview summary with highlights and match score |
| `scorecard`            | object | Detailed question-by-question scoring                        |
| `communication`        | object | Communication and speech quality evaluation                  |
| `transcript`           | object | Full interview transcript                                    |
| `audio_recording_link` | string | URL to the audio recording of the interview                  |
| `video_recording_link` | string | URL to the video recording (if video enabled)                |

#### `summary` Object

| Field          | Type    | Description                                    |
| -------------- | ------- | ---------------------------------------------- |
| `interview_id` | string  | Interview identifier                           |
| `highlights`   | array   | List of candidate strengths (strings)          |
| `lowlights`    | array   | List of candidate weaknesses or gaps (strings) |
| `match_score`  | integer | Overall match score (0-100)                    |

#### `scorecard` Object

| Field           | Type   | Description                                |
| --------------- | ------ | ------------------------------------------ |
| `interview_id`  | string | Interview identifier                       |
| `match_details` | array  | List of match assessment details (strings) |
| `questions`     | array  | List of scored question objects            |
| `tags`          | array  | Skill tags identified during the interview |

#### `scorecard.questions[]` Object

| Field                 | Type    | Description                                       |
| --------------------- | ------- | ------------------------------------------------- |
| `question_id`         | string  | Unique identifier for the question                |
| `question`            | string  | The interview question text                       |
| `evaluation_summary`  | string  | AI-generated summary of the candidate's answer    |
| `evaluation_criteria` | string  | The criteria used for evaluation                  |
| `preview_timestamp`   | integer | Timestamp offset (seconds) for the answer preview |
| `evaluation_score`    | integer | Score for this question (1-10)                    |
| `rank`                | integer | Ranking position of this question                 |
| `chat_transcript`     | array   | Relevant transcript excerpts for this question    |
| `score_of_1`          | string  | Description of what constitutes the lowest score  |
| `score_of_5`          | string  | Description of what constitutes the highest score |
| `score_weight`        | integer | Weight multiplier for this question's score       |
| `not_scored`          | boolean | Whether this question was excluded from scoring   |

#### `chat_transcript[]` Object

| Field           | Type    | Description                                      |
| --------------- | ------- | ------------------------------------------------ |
| `speaker`       | string  | Who spoke (`Candidate`, `bot`, `user`)           |
| `text`          | string  | The spoken text                                  |
| `timestamp`     | integer | Timestamp offset in seconds from interview start |
| `timestamp_str` | string  | Human-readable timestamp (e.g., `"04:34"`)       |

#### `communication` Object

| Field          | Type        | Description                                             |
| -------------- | ----------- | ------------------------------------------------------- |
| `interview_id` | string      | Interview identifier                                    |
| `speech_score` | float       | Overall speech/communication score (`-1` if not scored) |
| `evaluations`  | array       | List of communication evaluation objects (can be empty) |
| `template_id`  | string/null | Template ID used for evaluation (if applicable)         |

#### `transcript` Object

| Field          | Type   | Description                      |
| -------------- | ------ | -------------------------------- |
| `interview_id` | string | Interview identifier             |
| `transcript`   | array  | List of transcript entry objects |

***

#### `interview_data_model` Object

| Field                   | Type        | Description                                                 |
| ----------------------- | ----------- | ----------------------------------------------------------- |
| `interview_id`          | string      | Unique identifier for the interview                         |
| `details`               | object      | Core interview details (candidate info, workflow, metadata) |
| `company_info`          | object      | Full company/workspace configuration                        |
| `posting_info`          | object      | Full job posting configuration                              |
| `web_interview_info`    | object/null | Web interview analysis (duplicated from `agentic_data`)     |
| `resume_interview_info` | object/null | Resume screening analysis (`null` if no resume step)        |
| `sms_interview_info`    | object/null | SMS screening analysis (`null` if no SMS step)              |
| `form_interview_info`   | object/null | Form screening analysis (`null` if no form step)            |

#### `interview_data_model.details` Object

| Field                     | Type        | Description                                            |
| ------------------------- | ----------- | ------------------------------------------------------ |
| `interview_id`            | string      | Interview identifier                                   |
| `candidate_id`            | string      | Unique identifier for the candidate                    |
| `interviewed_on`          | float       | Unix timestamp when the interview was conducted        |
| `score`                   | float       | Overall score                                          |
| `name`                    | string      | Candidate's full name                                  |
| `email`                   | string      | Candidate's email address                              |
| `actions`                 | object/null | Available actions for this interview                   |
| `status`                  | string/null | Interview status                                       |
| `workflow_status`         | object      | Detailed workflow step statuses                        |
| `metadata`                | object      | Per-stage metadata (resume, web\_interview, sms, form) |
| `candidate_feedback`      | object/null | Candidate's feedback on the interview                  |
| `candidate_next_steps`    | object/null | Next steps for the candidate                           |
| `candidate_interview_url` | string      | The candidate-facing interview URL                     |

#### `workflow_status` Object

| Field             | Type        | Description                                      |
| ----------------- | ----------- | ------------------------------------------------ |
| `interview_id`    | string      | Interview identifier                             |
| `step_by_step`    | array       | List of workflow step status objects             |
| `all_complete`    | boolean     | Whether all required steps are complete          |
| `last_engagement` | float       | Unix timestamp of the last candidate interaction |
| `metadata`        | object/null | Additional workflow metadata                     |

#### `workflow_status.step_by_step[]` Object

| Field                    | Type        | Description                                                       |
| ------------------------ | ----------- | ----------------------------------------------------------------- |
| `complete`               | boolean     | Whether this step is complete                                     |
| `required`               | boolean     | Whether this step is required                                     |
| `order`                  | integer     | Position in the workflow sequence (1-based)                       |
| `id`                     | string      | Step identifier (e.g., `web_interview`, `resume_upload`)          |
| `started`                | boolean     | Whether the candidate has started this step                       |
| `details`                | object/null | Human-readable status details (`details`, `title`, `description`) |
| `raw_status`             | string      | Internal status code (e.g., `INTERVIEW_PROCESSED_RAW_FILES`)      |
| `last_updated_timestamp` | float       | Unix timestamp of the last update to this step                    |
| `knocked_out`            | boolean     | Whether the candidate was disqualified at this step               |
| `completion_percentage`  | float       | Step completion progress (0 to 1)                                 |
| `completion_metadata`    | object/null | Additional completion metadata                                    |
| `is_analyzing_call`      | boolean     | Whether AI analysis is still in progress                          |
| `total_duration`         | float/null  | Total duration of the step in seconds                             |
| `state_flags`            | object      | Boolean state flags for the step                                  |

#### `state_flags` Object

| Field               | Type    | Description                               |
| ------------------- | ------- | ----------------------------------------- |
| `is_completed`      | boolean | Whether the step is fully completed       |
| `is_incomplete`     | boolean | Whether the step is incomplete            |
| `is_analyzing_call` | boolean | Whether AI analysis is in progress        |
| `is_in_progress`    | boolean | Whether the step is currently in progress |

#### `details.metadata` Object

| Field           | Type        | Description                                                 |
| --------------- | ----------- | ----------------------------------------------------------- |
| `resume`        | object/null | Resume metadata (`null` if no resume step)                  |
| `web_interview` | object/null | Web interview metadata (score, highlights, recording links) |
| `sms`           | object/null | SMS screening metadata (`null` if no SMS step)              |
| `form`          | object/null | Form screening metadata (`null` if no form step)            |

#### `details.metadata.web_interview` Object

| Field                  | Type   | Description                  |
| ---------------------- | ------ | ---------------------------- |
| `final_score`          | float  | Final interview score        |
| `skill_highlights`     | array  | List of skill tags (strings) |
| `audio_recording_link` | string | URL to audio recording       |
| `video_recording_link` | string | URL to video recording       |

#### `company_info` Object

| Field                                       | Type         | Description                                               |
| ------------------------------------------- | ------------ | --------------------------------------------------------- |
| `company_id`                                | string       | Unique company identifier                                 |
| `workspace_id`                              | string       | Workspace identifier                                      |
| `created_at`                                | float        | Unix timestamp when the company was created               |
| `updated_at`                                | float/null   | Unix timestamp of last update                             |
| `created_by`                                | string       | Creator identifier                                        |
| `name`                                      | string       | Company name                                              |
| `image_url`                                 | string       | Company logo URL                                          |
| `intro_message`                             | string/null  | Custom intro message                                      |
| `company_background`                        | string/null  | Company background info                                   |
| `voice_id`                                  | string/null  | Default voice ID for interviews                           |
| `knowledge_base`                            | object/null  | Company knowledge base configuration                      |
| `interview_domain`                          | string/null  | Custom interview domain                                   |
| `interview_domain_id`                       | string/null  | Custom interview domain ID                                |
| `sender_email_id`                           | string/null  | Sender email configuration ID                             |
| `address`                                   | string/null  | Company address                                           |
| `address_data`                              | object/null  | Structured address data                                   |
| `timezone`                                  | string/null  | Company timezone                                          |
| `base_web_interview_config`                 | object       | Default web interview config (backdrop, avatar, branding) |
| `message_settings`                          | object/null  | Messaging configuration                                   |
| `sms_agent_config`                          | object/null  | SMS agent configuration                                   |
| `pdf_generation_config`                     | object/null  | PDF report generation settings                            |
| `cheat_detection`                           | object/null  | Cheat detection configuration                             |
| `allow_white_labeling`                      | boolean/null | Whether white labeling is enabled                         |
| `disable_all_candidate_communications`      | boolean/null | Whether all candidate communications are disabled         |
| `interview_completion_confidence_threshold` | float        | Confidence threshold for interview completion (0-1)       |
| `retention_policy_days`                     | integer/null | Data retention policy in days                             |
| `video_cheat_detection_enabled`             | boolean/null | Whether video cheat detection is enabled                  |
| `form_agent_enabled`                        | boolean/null | Whether form agent is enabled                             |
| `calendar_management_enabled`               | boolean/null | Whether calendar management is enabled                    |
| `email_templates_enabled`                   | boolean/null | Whether email templates are enabled                       |
| `phone_calls_enabled`                       | boolean/null | Whether phone calls are enabled                           |
| `profile_picture_processing`                | boolean/null | Whether profile picture processing is enabled             |
| `chat_agent_enabled`                        | boolean/null | Whether chat agent is enabled                             |
| `copilot_enabled`                           | boolean/null | Whether copilot is enabled                                |
| `design_templates_enabled`                  | boolean/null | Whether design templates are enabled                      |
| `interview_templates_enabled`               | boolean/null | Whether interview templates are enabled                   |
| `sourcing_configs_enabled`                  | boolean/null | Whether sourcing configs are enabled                      |
| `pbac_enabled`                              | boolean/null | Whether permissions-based access control is enabled       |
| `email_report_to_candidate_enabled`         | boolean/null | Whether email reports to candidates are enabled           |

#### `posting_info` Object

| Field                       | Type         | Description                                      |
| --------------------------- | ------------ | ------------------------------------------------ |
| `posting_id`                | string       | Unique posting identifier                        |
| `title`                     | string       | Job title                                        |
| `name`                      | string       | URL-friendly posting name                        |
| `internal_name`             | string/null  | Internal posting name                            |
| `description`               | string       | Job description (HTML)                           |
| `root_level_configs`        | object       | Top-level agent configuration                    |
| `company_id`                | string       | Company identifier                               |
| `created_at`                | float        | Unix timestamp when the posting was created      |
| `last_updated`              | float        | Unix timestamp when the posting was last updated |
| `archived`                  | boolean      | Whether the posting is archived                  |
| `deadline`                  | float        | Posting deadline (Unix timestamp)                |
| `finalized`                 | boolean      | Whether the posting is finalized                 |
| `test_posting`              | boolean      | Whether this is a test posting                   |
| `agentic_workflow`          | array        | List of agent workflow steps with configurations |
| `step_workflow`             | object       | Workflow definition with steps and dependencies  |
| `phone_number_id`           | string/null  | Associated phone number ID                       |
| `sender_email_id`           | string       | Sender email ID                                  |
| `email_template_group_id`   | string/null  | Email template group ID                          |
| `design_template_group_id`  | string/null  | Design template group ID                         |
| `sourcing_config_ids`       | array        | List of sourcing config IDs                      |
| `redirect_url`              | string/null  | Post-interview redirect URL                      |
| `scheduling_url`            | string/null  | Scheduling page URL                              |
| `event_slug`                | string/null  | Calendar event slug                              |
| `cal_team_id`               | string/null  | Calendar team ID                                 |
| `team_slug`                 | string/null  | Team slug                                        |
| `mascot_url`                | string       | Mascot image URL                                 |
| `mascot_id`                 | string       | Mascot identifier                                |
| `days_to_complete`          | integer/null | Days allowed to complete the interview           |
| `ats_metadata`              | object/null  | ATS integration metadata                         |
| `allow_sms_comms`           | boolean      | Whether SMS communications are enabled           |
| `max_retakes`               | integer/null | Maximum number of interview retakes              |
| `retake_cooldown_days`      | integer/null | Cooldown period between retakes                  |
| `is_template`               | boolean      | Whether this posting is a template               |
| `email_report_to_candidate` | boolean      | Whether to email reports to candidates           |

***

## Setting Up Webhooks

### Step 1: Prepare Your Endpoint

Your webhook endpoint must be a publicly accessible URL that can receive HTTP requests. The endpoint should:

* Accept `POST` (or `GET`) requests
* Return a `200` status code to acknowledge receipt
* Process the payload asynchronously if heavy processing is required

**Example endpoint (Node.js / Express):**

```javascript
app.post("/webhook/heymilo", (req, res) => {
  const event = req.body;

  console.log(`Interview ID: ${event.interview_id}`);
  console.log(`Posting ID: ${event.posting_id}`);

  // Process the event asynchronously
  processWebhookEvent(event);

  // Acknowledge receipt immediately
  res.status(200).json({ received: true });
});
```

**Example endpoint (Python / FastAPI):**

```python
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook/heymilo")
async def handle_webhook(request: Request):
    event = await request.json()

    print(f"Interview ID: {event['interview_id']}")
    print(f"Posting ID: {event['posting_id']}")

    # Process the event
    await process_webhook_event(event)

    return {"received": True}
```

### Step 2: Register the Webhook

Use the Create Webhook API to register your endpoint for the desired event type and job posting.

```bash
curl -X POST "https://api.heymilo.network/api/webhook/create" \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "posting_id": "your_posting_id",
    "url": "https://your-domain.com/webhook/heymilo",
    "event_type": "interview_completed",
    "http_method": "post"
  }'
```

### Step 3: Verify Delivery

After registering, trigger a test event (e.g., start an interview for a test candidate) and confirm your endpoint receives the payload. Check the response status and payload structure to ensure your integration handles the data correctly.

***

## Multiple Webhooks Per Posting

You can register multiple webhooks for the same posting to subscribe to different event types or send events to different endpoints.

**Example: Subscribe to all events for a single posting**

```bash
# Webhook for interview started
curl -X POST "https://api.heymilo.network/api/webhook/create" \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "posting_id": "posting_123",
    "url": "https://your-domain.com/webhook/started",
    "event_type": "interview_started"
  }'

# Webhook for interview completed
curl -X POST "https://api.heymilo.network/api/webhook/create" \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "posting_id": "posting_123",
    "url": "https://your-domain.com/webhook/completed",
    "event_type": "interview_completed"
  }'

# Webhook for report available
curl -X POST "https://api.heymilo.network/api/webhook/create" \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "posting_id": "posting_123",
    "url": "https://your-domain.com/webhook/report",
    "event_type": "report_available"
  }'
```

***

## Best Practices

<details>

<summary><strong>Respond Quickly</strong></summary>

Return a `200` status code as soon as you receive the webhook. Perform any heavy processing (database writes, downstream API calls, etc.) asynchronously to avoid timeouts.

</details>

<details>

<summary><strong>Handle Duplicate Events</strong></summary>

Use the `interview_id` field to deduplicate events. In rare cases, the same event may be delivered more than once. Design your handler to be idempotent.

</details>

<details>

<summary><strong>Validate Payloads</strong></summary>

Always validate the structure of incoming payloads before processing. Check for required fields and handle missing or unexpected data gracefully. Many fields can be `null` depending on the interview configuration.

</details>

<details>

<summary><strong>Use HTTPS Endpoints</strong></summary>

Always use HTTPS URLs for your webhook endpoints to ensure data is encrypted in transit. HeyMilo sends sensitive candidate information in webhook payloads.

</details>

<details>

<summary><strong>Monitor Webhook Health</strong></summary>

Log all incoming webhook requests and monitor for failures. If your endpoint becomes unreachable, events may be missed. Consider implementing alerting for webhook processing errors.

</details>

<details>

<summary><strong>Scope Webhooks to Postings</strong></summary>

Register webhooks only for the job postings you need to monitor. This reduces noise and ensures your endpoint only receives relevant events.

</details>

<details>

<summary><strong>Handle Nullable Fields</strong></summary>

Many fields in the `report_available` payload can be `null` depending on the posting configuration (e.g., `resume_interview_info`, `sms_interview_info`, `form_interview_info`). Always check for `null` before accessing nested properties.

</details>

***

## Common Use Cases

<details>

<summary><strong>ATS Sync</strong></summary>

Use `interview_completed` or `report_available` webhooks to automatically push interview results back to your Applicant Tracking System. Extract the score, workflow status, and report data to update candidate records in real time.

</details>

<details>

<summary><strong>Recruiter Notifications</strong></summary>

Trigger Slack, email, or SMS notifications to recruiters when candidates complete interviews. Use the `interview_completed` event to alert team members and include the candidate name, score, and a link to the full report.

</details>

<details>

<summary><strong>Automated Candidate Progression</strong></summary>

Use `report_available` to automatically advance or reject candidates based on their scores. For example, candidates with a `match_score` above a threshold can be moved to the next stage, while those below can receive an automated rejection email.

</details>

<details>

<summary><strong>Analytics &#x26; Dashboards</strong></summary>

Stream webhook events into your analytics platform to build custom hiring dashboards. Track metrics like time-to-complete, average scores, and candidate throughput across postings.

</details>

<details>

<summary><strong>Workflow Automation (Zapier / Make)</strong></summary>

Use webhook URLs as triggers in Zapier, Make, or other automation platforms. This enables no-code workflows like adding candidates to Google Sheets, sending calendar invites, or updating CRM records when interviews are completed.

</details>

***

## Error Handling

If your endpoint returns a non-`200` status code, the webhook delivery is considered failed. Ensure your endpoint is reliable and returns `200` promptly.

### Common Errors

| Scenario               | Recommendation                                                        |
| ---------------------- | --------------------------------------------------------------------- |
| Endpoint returns `4xx` | Check your endpoint URL and ensure it accepts the correct HTTP method |
| Endpoint returns `5xx` | Investigate server-side errors in your application                    |
| Endpoint unreachable   | Verify your endpoint is publicly accessible and DNS is configured     |
| Payload parsing fails  | Validate the `Content-Type` header and JSON body parsing logic        |

***

## Support

For webhook support and questions:

* Email: <support@heymilo.ai>
* Documentation: <https://docs.heymilo.cloud>
* Status Page: <https://status.heymilo.ai>


---

# 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.admin.heymilo.ai/webhook-management/webhook-documentation.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.
