Webhooks

Introduction

When webhooks are enabled from the Settings page of the TalkJS dashboard, your server will receive HTTP POST requests from TalkJS server notifying you about events that happen in your application between your users. Webhooks can be useful as part of a chatbot interface, to inspect messages as they happen or to be notified of certain events to trigger custom domain specific actions.

For testing the webhooks locally, you can check out our tutorial on how to integrate ngrok with talkjs.

TalkJS sends webhook events as HTTP POST requests with a JSON payload. By default, TalkJS sends a HTTP POST request and waits for a response from your webhook endpoint for 5 seconds. If TalkJS receives a non-200 OK response we will retry sending the event once more shortly after the first attempt failed.

If you expect a high amount of usage, then we strongly recommend that you immediately respond with a HTTP 200 OK and handle the webhook event asynchronously. This ensures that events are acknowledged and aren't incorrectly retried. If you handle events synchronously, it may cause your endpoint to become unresponsive when a surge of events are generated by your TalkJS account.

We advise you to check the authenticity of each event sent to your webhook endpoint, as explained in our security recommendations guide.

Webhook events

TalkJS supports following webhook events:

Occurs when a user is created in TalkJS backend.

Occurs when a user is updated in TalkJS backend.

Occurs when a message is sent in a conversation.

Occurs when another user in the same conversation receives the message in the chat UI. This event gets sent at most once for every member of a conversation other than the sender.

Note that message.read is only sent for conversations with at most 300 participants, in which case the readBy field contains up to 300 elements. For conversations with 301 or more participants, no message.read webhook is sent.

Occurs when the content or metadata of the message is updated. Note that message.updated is not called when the message has been read (message.read).

Occurs when the message has been deleted.

Occurs when a conversation is deleted via the REST API

Occurs when TalkJS decided that a notification is to be sent to a user. In particular, this is the case when both of the following conditions are met:

  1. One or more messages have been sent by one or more users and the user(s) stopped typing or went offline, indicating that no further messages are to be expected
  2. Another user was not currently online and looking at the same conversation, indicating that they might have missed the messages sent and thus should be notified.

This webhook event is triggered even if notify is set to false for individual participants. This makes it possible to create additional custom logic for if each participant should be notified. Using the webhook payload data you can check the participants notify setting within the webhook payload itself and determine if the notification should be sent or dropped, for example:

1const conversation = event.data.conversation;
2 const recipient = event.data.recipient;
3 if(conversation.participants[recipient.id].notify) { ... }

Note that the notification.triggered event will occur even if a user has no email or phone number set. This allows you to substitute TalkJS's notification features by your own, if you wish.

Occurs when TalkJS has actually sent the aforementioned notification. May be triggered more than once for each notification if TalkJS notifies a user both via email and SMS. Does not get triggered for mobile push notifications.

Occurs when a email was successfully delivered.

Occurs when an email was not successfully delivered. It is triggered for bounces and spam complaints that resulted from outgoing email.

Occurs when a user opens an email notification in an email client. Note that it may not always be triggered, because user’s email client might be blocking tracking pixels.

Occurs when a user clicks a link inside an email notification.

Prefer examples?

You can see examples on how to use TalkJS Webhooks in our examples repository on GitHub.

Event structure

All events have the same top-level structure, as shown below. Only the structure of the data field differs for every type:

1type Event = {
2 id: string;
3 createdAt: number; // UNIX timestamp in milliseconds
4 data: any;
5 type: string;
6};

The supported webhook events are structured below.

User created

The structure for the user.created event is as follows:

1type UserCreatedEvent = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 user: User;
6 };
7};

User updated

The structure for the user.updated event is as follows:

1type UserUpdatedEvent = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 user: User;
6 };
7 type: 'user.updated';
8};

Conversation deleted

The structure for the conversation.deleted event is as follows:

1type ConversationDeletedEvent = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 conversation: Conversation;
6 };
7 type: 'conversation.deleted';
8};

Message sent

The structure for the message.sent event is as follows:

1type MessageSentEvent = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 sender: User;
6 conversation: Conversation;
7 message: Message;
8 };
9 type: 'message.sent';
10};

Message read

The structure for the message.read event is as follows:

1type MessageReadEvent = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 recipient: User;
6 conversation: Conversation;
7 message: Message;
8 };
9 type: 'message.read';
10};

Message updated

The structure for the message.updated event is as follows:

1type MessageUpdatedEvent = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 sender: User;
6 conversation: Conversation;
7 message: Message;
8 };
9 type: 'message.updated';
10};

Message deleted

The structure for the message.deleted event is as follows:

1type MessageDeleted = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 sender: User | null;
6 conversation: Conversation;
7 message: Message;
8 };
9 type: 'message.deleted';
10};

Notification triggered

The structure for the notification.triggered event is as follows:

1type NotificationTriggered = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 notificationId: string;
6 recipient: User;
7 sender: User;
8 conversation: Conversation;
9 messages: Message[];
10 };
11 type: 'notification.triggered';
12};

Note that the notification.triggered event will occur even if a user has no email or phone number set. This allows you to substitute TalkJS's notification features by your own, if you wish.

Notification sent

The structure for the notification.sent event is as follows:

1type NotificationSent = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 notificationId: string;
6 recipient: User;
7 sender: User;
8 conversation: Conversation;
9 messages: Message[];
10 channel: 'email' | 'sms';
11 };
12 type: 'notification.sent';
13};

Notification delivered

The structure for the notification.delivered event is as follows:

1type NotificationDelivered = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 notificationId: NotificationId;
6 conversation: Conversation;
7 messages: Message[];
8 recipient: User;
9 sender: User;
10 channel: 'email';
11 };
12 type: 'notification.delivered';
13};

Notification bounced

The structure for the notification.bounced event is as follows:

1type NotificationBounced = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 notificationId: NotificationId;
6 conversation: Conversation;
7 messages: Message[];
8 recipient: User;
9 sender: User;
10 channel: 'email';
11 };
12 type: 'notification.bounced';
13};

Notification opened

The structure for the notification.opened event is as follows:

1type NotificationOpened = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 notificationId: NotificationId;
6 conversation: Conversation;
7 messages: Message[];
8 recipient: User;
9 sender: User;
10 channel: 'email';
11 isFirstOpen: boolean
12 };
13 type: 'notification.opened';
14};

The structure for the notification.link.clicked event is as follows:

1type NotificationLinkClicked = {
2 id: string;
3 createdAt: UnixMilliseconds;
4 data: {
5 notificationId: NotificationId;
6 conversation: Conversation;
7 messages: Message[];
8 recipient: User;
9 sender: User;
10 channel: 'email';
11 originalUrl: string;
12 };
13 type: 'notification.link.clicked';
14};

Interfaces

The interfaces of the types presented above are the following:

1type UserId = string;
2type ConversationId = string;
3type MessageId = string;
4type UnixMilliseconds = number;
5type ByteSize = number;
6
7type Message = {
8 id: MessageId;
9 conversationId: ConversationId;
10 type: 'UserMessage' | 'SystemMessage';
11 readBy: UserId[];
12 senderId: UserId;
13 text?: string;
14 attachment?: File;
15 origin: 'web' | 'rest' | 'email' | 'import';
16 location?: Coordinates;
17 createdAt: UnixMilliseconds;
18};
19
20type Conversation =
21{
22 id: ConversationId;
23 subject: string | null;
24 photoUrl: string | null;
25 welcomeMessages: string[] | null;
26 custom: { [name: string]: string };
27 lastMessage: Message | null;
28 participants: {
29 [id: UserId]: { access: 'ReadWrite' | 'Read'; notify: boolean };
30 };
31 createdAt: UnixMilliseconds;
32};
33
34type User = {
35 id: UserId;
36 name: string;
37 welcomeMessage: string;
38 photoUrl: string;
39 role: string;
40 email: string[] | null;
41 phone: string[] | null;
42 custom: { [name: string]: string };
43 availabilityText: string;
44 locale: string;
45 createdAt: UnixMilliseconds;
46 pushTokens: { [token_id: string]: true | null } | null;
47};
48
49type File = {
50 url: string;
51 size: ByteSize;
52};
53
54type Coordinates = [
55 number, // latitude
56 number // longitude
57];