Webhooks
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.
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:
- 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
- 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.
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 milliseconds4 data: any;5 type: string;6};
The supported webhook events are structured below.
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};
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};
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};
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};
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};
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};
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};
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.
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};
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};
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};
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: boolean12 };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};
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;67type 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};1920type 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};3334type 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};4849type File = {50 url: string;51 size: ByteSize;52};5354type Coordinates = [55 number, // latitude56 number // longitude57];