Webhook Types
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
- You configure a URL for receiving webhooks (via API or the Kinescope interface)
- When an event occurs, Kinescope sends an HTTP POST request to your URL with JSON data
- 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 processinguploading— video is uploadingpre-processing— video pre-processingprocessing— video is being processedaborted— processing was aborteddone— video is ready to watcherror— an error occurred during processingsuspended— 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 meetingscall_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 IDidentity— the call connection identifier; use it to match a participant’s join and leavename— the name the participant joined withrole— 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 successfullyfailed— recording finished with an erroraborted— recording was abortedlimit_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?
- General API guidelines — authorization and request format
- API Reference — full API documentation for webhook configuration
- File upload via API — automated video upload
Still have questions? Write to the support chat within the Kinescope interface — our specialists will help!