Skip navigation

Webhook Types

Updated: 03.10.2026
Open as Markdown

Kinescope supports outgoing webhooks — notifications about events that occur with your videos, streams, or Speak meetings. When an event occurs (e.g., a video is processed or a stream ends), Kinescope sends an HTTP request to the URL you specified.

Who this article is for

  • Developers — need to automate processes in their system when Kinescope events occur
  • Platform administrators — need to receive notifications about video processing status
  • DevOps engineers — need to integrate Kinescope with monitoring systems

What problems webhooks solve

Webhooks let you automate processes in your system:

  • Tracking video processing — know when a video is ready to watch or an error occurred
  • Stream monitoring — receive notifications about streamer connections, stream ends, and other events
  • Tracking Speak meetings — know when calls start and end, when participants join and leave, and when a meeting is recorded
  • Integration with your system — automatically update statuses in your database or send notifications to users

How it works

  1. You configure a URL for receiving webhooks (via API or the Kinescope interface)
  2. When an event occurs, Kinescope sends an HTTP POST request to your URL with JSON data
  3. Your server handles the request and performs the needed actions (status updates, sending notifications, etc.)

Video webhooks

media.update.status

Sent when the video status is updated. Used to track video processing, errors, or publication completion.

Example 1: Successful status update

{
  "event": "media.update.status",
  "data": {
    "id": "7127f2d7-0e96-40d0-9a03-2e987c096466",
    "status": "done"
  }
}

Example 2: Processing error

{
  "event": "media.update.status",
  "data": {
    "id": "12706830-0e96-40d0-9a03-2e987c096466",
    "status": "error",
    "message": "import error: code=610100, message=cannot download link: https://example.ru/test.mp4, http_code=404"
  }
}

Possible statuses:

  • pending — video is waiting for processing
  • uploading — video is uploading
  • pre-processing — video pre-processing
  • processing — video is being processed
  • aborted — processing was aborted
  • done — video is ready to watch
  • error — an error occurred during processing
  • suspended — processing/uploading is paused

Stream webhooks

live.created

Notification about the creation of a new stream event (via API or interface).

{
  "event": "live.created",
  "data": {
    "event_id": "abc123-def456-ghi789"
  }
}

live.connected

Streamer connected — RTMP stream started arriving at the server.

{
  "event": "live.connected",
  "data": {
    "event_id": "abc123-def456-ghi789"
  }
}

live.disconnected

Streamer disconnected — RTMP stream stopped.

{
  "event": "live.disconnected",
  "data": {
    "event_id": "abc123-def456-ghi789"
  }
}

live.finished

Stream ended. The response also includes video_id — the ID of the stream recording video (if recording was enabled).

{
  "event": "live.finished",
  "data": {
    "event_id": "abc123-def456-ghi789",
    "video_id": "7127f2d7-0e96-40d0-9a03-2e987c096466"
  }
}

live.cancelled

Stream was cancelled.

{
  "event": "live.cancelled",
  "data": {
    "event_id": "abc123-def456-ghi789"
  }
}

live.enabled

Stream is available for viewing by clients.

{
  "event": "live.enabled",
  "data": {
    "event_id": "abc123-def456-ghi789"
  }
}

Speak webhooks

Events about calls in Speak rooms: a meeting starting and ending, participants joining and leaving, and recording. To receive them, list the events you need in the events field when you create or update a webhook.

Every Speak event carries two identifiers:

  • room_id — the room ID. A room is permanent and can host many meetings
  • call_id — the call ID, that is, a single meeting in this room

These are the same IDs as in the Speak API reference: use them to request room and call data.

speak.call.started

A call started in the room.

{
  "event": "speak.call.started",
  "data": {
    "room_id": "0f8c3a52-6b1e-4c2d-9a47-3e5b8d1f2c60",
    "call_id": "5d2e9b14-7a3c-4f8e-b061-92c4a7e3d815"
  }
}

speak.call.finished

The call in the room ended.

{
  "event": "speak.call.finished",
  "data": {
    "room_id": "0f8c3a52-6b1e-4c2d-9a47-3e5b8d1f2c60",
    "call_id": "5d2e9b14-7a3c-4f8e-b061-92c4a7e3d815"
  }
}

speak.participant.joined

A participant joined the call. The participant object contains:

  • id — the room participant ID
  • identity — the call connection identifier; use it to match a participant’s join and leave
  • name — the name the participant joined with
  • role — the participant’s role in the room

id and role may be empty — for example, if the participant’s session has already closed by the time the event is sent. Service participants, such as the recording bot, don’t trigger speak.participant.* events.

{
  "event": "speak.participant.joined",
  "data": {
    "room_id": "0f8c3a52-6b1e-4c2d-9a47-3e5b8d1f2c60",
    "call_id": "5d2e9b14-7a3c-4f8e-b061-92c4a7e3d815",
    "participant": {
      "id": "c41f7e28-3b9d-4a65-8e12-6f0d5b9a7c33",
      "identity": "8e3b5a17-2c4d-4f90-a6e1-7d9c0b3f5a24",
      "name": "Anna Smith",
      "role": "admin"
    }
  }
}

speak.participant.left

A participant left the call. The participant object is the same as in speak.participant.joined.

{
  "event": "speak.participant.left",
  "data": {
    "room_id": "0f8c3a52-6b1e-4c2d-9a47-3e5b8d1f2c60",
    "call_id": "5d2e9b14-7a3c-4f8e-b061-92c4a7e3d815",
    "participant": {
      "id": "c41f7e28-3b9d-4a65-8e12-6f0d5b9a7c33",
      "identity": "8e3b5a17-2c4d-4f90-a6e1-7d9c0b3f5a24",
      "name": "Anna Smith",
      "role": "admin"
    }
  }
}

speak.record.started

Meeting recording started. The record object contains the recording ID (id) and its status (status).

{
  "event": "speak.record.started",
  "data": {
    "room_id": "0f8c3a52-6b1e-4c2d-9a47-3e5b8d1f2c60",
    "call_id": "5d2e9b14-7a3c-4f8e-b061-92c4a7e3d815",
    "record": {
      "id": "3a9e6c21-5f7b-4d18-9c0e-b2a4d6f8e137",
      "status": "starting"
    }
  }
}

speak.record.finished

Meeting recording stopped. The event has no video ID: the recording is saved to the catalog and processed like any other video.

{
  "event": "speak.record.finished",
  "data": {
    "room_id": "0f8c3a52-6b1e-4c2d-9a47-3e5b8d1f2c60",
    "call_id": "5d2e9b14-7a3c-4f8e-b061-92c4a7e3d815",
    "record": {
      "id": "3a9e6c21-5f7b-4d18-9c0e-b2a4d6f8e137",
      "status": "complete"
    }
  }
}

Possible statuses:

  • complete — recording finished successfully
  • failed — recording finished with an error
  • aborted — recording was aborted
  • limit_reached — recording stopped because a limit was reached

Restreaming from Speak without recording doesn’t trigger speak.record.* events.

Webhook handling examples

Example 1: Updating video status in the database

Here is how to handle the media.update.status webhook and update the status in your database:

package main

import (
    "encoding/json"
    "log"
)

type MediaStatusEvent struct {
    Event string `json:"event"`
    Data  struct {
        ID      string `json:"id"`
        Status  string `json:"status"`
        Message string `json:"message,omitempty"`
    } `json:"data"`
}

func handleMediaStatusUpdate(event MediaStatusEvent) error {
    videoID := event.Data.ID
    status := event.Data.Status
    
    // Update status in the database
    // db.Exec("UPDATE videos SET status = ?, error_message = ?, updated_at = ? WHERE kinescope_id = ?",
    //     status, event.Data.Message, time.Now(), videoID)
    
    if status == "done" {
        notifyUser(videoID, "Your video is ready to watch!")
    }
    
    if status == "error" {
        log.Printf("Video processing error %s: %s", videoID, event.Data.Message)
    }
    
    if status == "aborted" {
        notifyUser(videoID, "Video processing was aborted")
    }
    
    return nil
}

Example 2: Handling stream end

When a stream ends, you can automatically process the recording:

package main

type LiveFinishedEvent struct {
    Event string `json:"event"`
    Data  struct {
        EventID string `json:"event_id"`
        VideoID string `json:"video_id,omitempty"`
    } `json:"data"`
}

func handleLiveFinished(event LiveFinishedEvent) error {
    eventID := event.Data.EventID
    videoID := event.Data.VideoID
    
    // Update stream status
    // db.Exec("UPDATE live_events SET status = ?, recording_video_id = ?, finished_at = ? WHERE kinescope_event_id = ?",
    //     "finished", videoID, time.Now(), eventID)
    
    if videoID != "" {
        notifyViewers(eventID, "Stream recording is available: "+videoID)
    }
    
    return nil
}

Example 3: Universal webhook handler

Here is an example of a universal handler that can process different webhook types:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type WebhookEvent struct {
    Event string          `json:"event"`
    Data  json.RawMessage `json:"data"`
}

func handleWebhook(event WebhookEvent) error {
    switch event.Event {
    case "media.update.status":
        var e MediaStatusEvent
        json.Unmarshal(event.Data, &e.Data)
        e.Event = event.Event
        return handleMediaStatusUpdate(e)
        
    case "live.finished":
        var e LiveFinishedEvent
        json.Unmarshal(event.Data, &e.Data)
        e.Event = event.Event
        return handleLiveFinished(e)
        
    default:
        log.Printf("Unknown event type: %s", event.Event)
    }
    
    return nil
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    var event WebhookEvent
    if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    if err := handleWebhook(event); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

Setting up webhooks

Webhooks are configured via the Kinescope API. Specify the URL of your endpoint that will receive notifications.

Important: Your endpoint must return HTTP 200 in response to successful webhook handling. If Kinescope receives an error (4xx, 5xx), it may retry the request.

Security

It is recommended to verify webhook authenticity:

  • Check the request source — make sure the request comes from Kinescope
  • Use HTTPS — webhooks should be sent to secure URLs
  • Validate data — check the format and required fields in the request

Done! You can now set up webhooks and automate processes in your system.

What’s next?

  1. General API guidelines — authorization and request format
  2. API Reference — full API documentation for webhook configuration
  3. File upload via API — automated video upload

Still have questions? Write to the support chat within the Kinescope interface — our specialists will help!