Events (via webhooks)

You can set listeners for server events via webhooks, to enable you to take certain server actions based on the meeting events and state changes.

Setting a webhook

You need to make an API call to add or remove a webhook for your organization. Please refer to Webhooks section under API reference to get the details.

curl --request POST \
  --url https://api.cluster.dyte.in/v1/organizations/myorgid/webhook \
  --header 'Accept: application/json' \
  --header 'Authorization: APIKEY your-api-key' \
  --header 'Content-Type: application/json' \
  --data '{"events":
["meeting.ended","meeting.started","recording.statusUpdate"],"name":"mywebhook","url":"https://myorganization/url"}'

Verifying webhooks using signature

Each webhook request is signed using Dyte's private key, and the same can be verified at your end by following the below steps.

  1. Get Dyte's public key: This can be done by making a GET request to this URL - https://api.cluster.dyte.in/.well-known/webhooks.json.
curl -X GET "https://api.cluster.dyte.in/.well-known/webhooks.json"
  1. Check for the signature header: Each incoming request should have a custom dyte-signature header. This is the signature value that you would be verifying in the next step.
  2. Verify the signature: The signature value is based on RSA-SHA256 digest of the payload. You can calculate the same at your end, and if the value matches the one supplied in the header as described by previous step, you should consider the webhook to be originating from the correct server.
    For example, you can do the following:
const signature = req.headers['dyte-signature'];
const payload = req.body;

const isVerified = crypto.verify(
    'RSA-SHA256',
    Buffer.from(JSON.stringify(payload)),
    dytePublicKey,
    Buffer.from(signature, 'base64'),
);

📘

JSON Stringification

Few languages like Python, and maybe if you are using a specific library for JSON processing, your JSON.stringify results may not be consistent. Please make sure that there are no such quirks as spaces in between the stringified JSON before trying to verify the webhook signature.

Webhooks

Each webhook request is an HTTPS POST request, where the details about the webhook are sent in the message body in JSON format.

In order to simplify the structure, we first define some types which are going to be used in the API responses later.

Meeting Interface

interface Meeting {
    organizedBy: string;
    id: string;
    title: string;
    roomName: string;
    status: string;
    createdAt: string;
}

Participant Interface

interface Participant {
    userDisplayName: string;
    peerId: string;
    clientSpecificId: string;
}

Recording Interface

interface Recording {
    recordingId: string;
    status: 'INVOKED' | 'UPLOADED' | 'ERRORED';
    downloadUrl?: string;
    downloadUrlExpiry?: string;
    errMessage?: string;
}

In the following sections, we describe each event and the kind of data that is sent along with the webhook.

meeting.started

This is fired when the first participant joins a meeting.

Type of Webhook Body

interface MeetingCreated {
    event: "meeting.started";
    meeting: Meeting;
}

Sample Webhook Body

{
    "event": "meeting.started",
    "meeting": {
        "id": "cae39473-ef23-4ca2-a9e9-98f1509354f2",
        "title": null,
        "roomName": "testing-room",
        "status": "LIVE",
        "createdAt": "2021-02-09T10:07:11.675Z",
        "organizedBy": "myOrganization"
    }
} 

meeting.ended

This is fired 2 seconds after all the participants leave the call. 2-second delay is there to make sure we don't end a meeting due to disconnects.

Type of Webhook Body

interface MeetingEnded {
    event: "meeting.ended";
    meeting: Meeting;
}

Sample Webhook Body

{
    "event": "meeting.ended",
    "meeting": {
        "id": "cae39473-ef23-4ca2-a9e9-98f1509354f2",
        "title": null,
        "roomName": "testing-room",
        "status": "LIVE",
        "createdAt": "2021-02-09T10:07:11.675Z",
        "organizedBy": "myOrganization"
    }
}

meeting.participantJoined

This is fired whenever a participant joins a meeting.

Type of Webhook Body

interface MeetingParticipantJoined {
    event: "meeting.participantJoined";
    meeting: Meeting;
    participant: Participant;
}

Sample Webhook Body

{
    "event": "meeting.participantJoined",
    "meeting": {
        "id": "cae39473-ef23-4ca2-a9e9-98f1509354f2",
        "title": null,
        "roomName": "testing-room",
        "status": "LIVE",
        "createdAt": "2021-02-09T10:07:11.675Z",
        "organizedBy": "myOrganization"
    },
    "participant": {
        "userDisplayName": "Rohan",
        "peerId": "7eef23c6-1985-492b-9b95-99bac37b60d7",
        "clientSpecificId": "a6b2ac87-6f45-46d9-b48a-7e9ccdfd7517"
    }
}

meeting.participantLeft

Get fired whenever a participant leaves a meeting. This event is also fired when one gets disconnected.

Type of Webhook Body

interface MeetingParticipantLeft {
    event: "meeting.participantLeft";
    meeting: Meeting;
    participant: Participant;
}

Sample Webhook Body

{
    "event": "meeting.participantLeft",
    "meeting": {
        "id": "cae39473-ef23-4ca2-a9e9-98f1509354f2",
        "title": null,
        "roomName": "testing-room",
        "status": "LIVE",
        "createdAt": "2021-02-09T10:07:11.675Z",
        "organizedBy": "myOrganization"
    },
    "participant": {
        "userDisplayName": "Rohan",
        "peerId": "7eef23c6-1985-492b-9b95-99bac37b60d7",
        "clientSpecificId": "a6b2ac87-6f45-46d9-b48a-7e9ccdfd7517"
    }
} 

recording.statusUpdate

Get fired whenever there is a status update for a meeting recording.

Type of Webhook Body

interface RecordingStatusUpdate {
    event: "recording.statusUpdate";
    meeting: Meeting;
    recording: Recording;
}

Sample Webhook Body

Webhook Body for recording started.

{
    "event": "recording.statusUpdate",
    "meeting": {
        "id": "cae39473-ef23-4ca2-a9e9-98f1509354f2",
        "title": null,
        "roomName": "testing-room",
        "status": "LIVE",
        "createdAt": "2021-02-09T10:07:11.675Z",
        "organizedBy": "myOrganization"
    },
    "recording": {
        "recordingId": "7a789a2d-1142-4be3-a208-4d25a75663e7",
        "status": "INVOKED"
    }
}

Webhook Body for recording uploaded.

{
    "event": "recording.statusUpdate",
    "meeting": {
        "id": "cae39473-ef23-4ca2-a9e9-98f1509354f2",
        "title": null,
        "roomName": "testing-room",
        "status": "LIVE",
        "createdAt": "2021-02-09T10:07:11.675Z",
        "organizedBy": "myOrganization"
    },
    "recording": {
        "recordingId": "7a789a2e-1141-4bd3-a908-4d30a75773e7",
        "status": "UPLOADED",
        "downloadUrl": "https://dyte-recordings.s3.ap-south-1.amazonaws.com/4086c8ec-d410-4707-a976-3021ea432afe/testing-room_1612865231994.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIA5YIELHA%2F20210209%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20210209T100821Z&X-Amz-Expires=432000&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEJL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCmFwLXNvdXRoLTEiSDBGAiEAuTSvvZqzO2pUqbDhARzAL3VFoyy0Y46EIzM7W%2Ff0VpcCIQC6tX%2BjoVyuZR2hBPkVoVi8Wjk2EgHjRso%2FyTieku6a7Cq%2FAwiL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDk0NDkwODYyMTQxMCIMYaJWugiFY9VEWG%2F8KpMDm0QQG%2BaorDTJWlYrWeDMMyZv74rxS2B6VOEAMM%DtDELOD9HFdK33wLiO8AUYoIOqm1Y7cN0xKNRjE%2BLT9F1Un3iFWH6Wjv09N6y9Nd4%2rZJaVajtKuxJW710HBOK6kCscKWgMuNwy7qpm042WdjOuwkN3E%2BIW9tbELWeQrWy0XOd6pVp7vhZuaVQqB0Vq1dQ2xz6kTMAKDGbPbM6qZnY7SzWdKzNsPaXqJD0H%2B8VMb0No4MaHVcKfWml3dgo2UIs6Vnyr0nzPywSqHpkOtBtDxzRdh0ch08x0A6EqAoBSEJallDy4dZ3koRqFg0GB00fZ%2FXrdcRRCPvlmaWM3NwCvdyIdC31d03dc0TdHfjonizte7Ph4qdnI2tiu5J%2Fq441f5t8FvY2au0LnjSXsFB8kjJcli%2B8n%2FmxQF%2FqA%2F2dsfWyOZb881oCr1Wwe0Gc6CumcZu1Rv%2Bs2eAFXltkqfpB29jwLAGTC0vYmBBjrqAYlY8VkIx4mD5DR3kRMaS9Stn1s5Tt79dljeEWBFtfSzDmwnC%2FQENhHsFofiBf516mh62OVk160vDw%2FIEfPZCRT6w9rq%2BxhWo86unyaFPKbludpxeHlo0evjIf1PM9LdapoSHMSRxk6hpbwNu5oODPFEsEHxjwPfTrsrQzEgg%2BusLCK%2BVSfQsLhN8oyZvOZKvK%2FmRBh3fORICjG4UcvQ%2FVkqpjFKgdfR5NAuBlJWEWwFwxz1fJ2Ix9USb3ygAMvkcI2C6%2BHVqzRYpONYsmrum3mJcL4SOpaMTgSMwfBmYhtPuFAZH8G5rUiJKA%3D%3D&X-Amz-Signature=1df1f6b43c86e7cbfb529deed007724ebac0fa6a0d884c9fa9179881d61541f4&X-Amz-SignedHeaders=host",
        "downloadUrlExpiry": "2021-02-14T10:08:21.917Z"
    }
} 

Webhook Body for recording error.

{
    "event": "recording.statusUpdate",
    "meeting": {
        "id": "cae39473-ef23-4ca2-a9e9-98f1509354f2",
        "title": null,
        "roomName": "testing-room",
        "status": "LIVE",
        "createdAt": "2021-02-09T10:07:11.675Z",
        "organizedBy": "myOrganization"
    },
    "recording": {
        "recordingId": "7a789a2e-1141-4bd3-a908-4d30a75773e7",
        "status": "ERRORED",
        "errMessage": "Description of the error."
    }
}