We introduced Version 2 of the Custom Channel API with an enhanced feature set in May 2023.
Version 1 of the Custom Channel API is deprecated and will only be supported until August 31, 2023.
Please make sure your integrations are compatible with the changes introduced in V2
With our custom channel you can easily connect various message-based systems to Userlike and complement the channels we already support. For example this could be your own email server.
Messages coming in through the custom channel appear in the Message Center and senders are identified as contacts through a unique contact identifier on your side, for example their email address or your customer ID. When your operators respond in Userlike, the contacts receive the reply on the message system you connect.
Your custom channel will use two JSON API endpoints. The endpoint on Userlike’s side is where you relay messages to and it’s set up automatically at channel creation. The second one lies on your message system’s end, it’s where you receive messages from your contacts. The custom channel supports text, voice and media messages, along with live preview for media links.
API versioning
Our APIs are constantly evolving, but we are striving to implement any new features in a backwards compatible way. If breaking changes are necessary, we will introduce a new API version. Examples for breaking changes are:
- Removing, renaming, or moving API entities.
- Changing or removing functionality.
- Making optional parameters or properties mandatory.
Non-breaking changes, like new endpoints or new optional properties, can happen anytime. When we deprecate older versions of the API, we will provide at least 90 days of notice.
The current version of our Custom Channel API is Version 2, which was released May 04, 2022.
The previous Version 1 is deprecated and will be disabled after August 31, 2023.
Changelog
2024-09-20
- We added the
is_deleted
property to themessage
object of outbound messages. When operators delete messages this property is added to the outgoing message notifying your Frontend that an operator message was deleted. The exact message which was deleted can be identified via the suppliedmsgid
property. Please note operator messages can only be soft deleted effectively preventing the contact from accessing them while they are still available but flagged as deleted in the UMC
- We added the
is_deleted
property to themessage
object of inbound messages. If you decide to allow message deletion in your frontend send this as part of the regular message containing the originalmsgid
and we will hard delete the corresponding message from our system.
2023-07-06
- We added the
event
property to themessage
object in outbound messages. This property is available when thetype
of themessage
isnotification
, and helps you distinguish between different types of notifications.
2023-05-04
- We released version 2 of our Custom Channel API. For a detailed description of the changes, refer to the next section.
Changes in V2
Please note that we did not update existing channels to V2 automatically yet and will support V1 until August 31, 2023. Once you have ensured that your endpoints are compatible with V2, you can update your channels to V2 in the Dashboard yourself.
New Features
- Use your most suitable contact identifiers, instead of having to use
email
.
- Support for starting separate conversations with the same contact.
- Update contact information through webhook calls.
- Prevent sending out duplicate messages by providing message UUIDs.
Inbound data changes
We overhauled the JSON packet structure to be able to add more functionality.
email
was replaced bycontact_identifier
, which can be any unique string identifier for your contact. You may still use an email, however note thatcontact_identifier
is a generic identifier. This means that emails will not be set on the contact automatically. For this, you can now provide the email inside thecontact
object in the payload.
- Added
conversation_identifier
, which allows you to generate an optional identifier for a conversation. If you do, you are able to start new conversations with the same contact.
- Added
uuid
field in message body, which enables you to prevent sending duplicated messages by accident.
- Added optional
contact
object, which allows setting certain information on the contact.
- Changed the attachment object structure, removing the
payload
nesting.
- Please note that the updated webhook URL now includes the version number. After updating your channel, you can find the correct URL ending with:
api/um/channel/custom/v2/webhook/
.
For examples and explanations please refer to the section on inbound messages below.
Outbound data changes
With V2 we reduced the information contained inside the outbound messages we send to your API endpoint:
conversation
was removed completely, you can usemessage.conversation_id
instead orconversation_identifier
if given (also see inbound documentation).
message
was reduced a lot and now only contains these keys:body
,conversation_id
,msgid
,sent_at
,type
andevent
(whentype
isnotification
).
operator
was reduced a lot and now only contains these keys:about_me
,display_name
,id
,job_title
andurl_image
.
contact
did not change.
contact_identifier
was added.
- optional
conversation_identifier
was added.
For examples please refer to the section on outbound messages below.
Channel setup
Start by creating a custom channel in the settings. There you fill out the following fields:
- Channel name: Name of your custom channel
- Outbound URL: URL of your API endpoint
- Outbound auth token (provided by Userlike): Authentication token to access your API endpoint
- Inbound auth token (provided by Userlike): Authentication token to access the Userlike API endpoint
- Inbound URL: URL of the Userlike API endpoint. It will be generated and appear in the channel settings after the channel setup
- Widget: Widget that will handle messages sent to the channel. The Widget’s settings apply to assign the right operator, display the correct language and use the right privacy settings.
After filling out the form, click Create channel.
API documentation
In the following sections you learn how to set up your API endpoint and how to access Userlike’s API endpoint. This should give you a straightforward understanding of how to get your custom channel up and running with your use case.
How to send inbound contact messages to the Userlike API endpoint
Safety
It is crucial to note that any provided identifier needs to be verified on your side, since we will interpret all incoming information as coming from this user. If e.g. you are using an email, make sure the user really owns this email and is authenticated with it on your end.
Contact Identifier
In
v2
, we have introduced a significant change involving the identifier used for contacts within the custom channel scope. The previous identifier, email
within the message
object, has been replaced with a new immutable identifier called contact_identifier
. This contact_identifier
is required and cannot be modified once it has been created.Conversation Identifier
You can assign any string value to the
conversation_identifier
within the payload object. The conversation_identifier
serves as an identifier for a conversation associated with a specific contact_identifier
. If a conversation cannot be found using the provided value, a new conversation will be initiated with it.Channel Scope
Similar to the
v1
version, contacts are scoped to a specific custom channel. In this context, each contact_identifier
and conversation_identifier
is exclusively associated with a particular custom channel. As a result, sending the same contact_identifier
or conversation_identifier
to two different channels will create two separate contacts or conversations for a contact, each specifically linked to their respective channels.Requests
To send a message to the custom channel you need to send a JSON packet as POST request to the Inbound URL endpoint, which you find in the channel’s settings.
Include an API-SECURITY-TOKEN header in the POST request, with the value of the Inbound auth token, which you also find in the channel’s settings.
Sending inbound messages to the Userlike API endpoint
javascript{ "attachments": [ { "description": "Test Image", "url": "https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg" } ], "contact": { "email": "jsmith@example.com", "name": "Jane Smith" }, "contact_identifier": "j_smith_1234", "conversation_identifier": "cff47d61-6d02-4f04-b596-ece293ab4719", "message": { "body": "Hello", "uuid": "a223420c-8fe6-4aed-bb21-3099fceff095" } }
Name | Description |
contact_identifier | required A string used to uniquely identify the contact within the custom channel scope. |
message | required Message object, containing body and uuid field. |
conversation_identifier | Optional Field. Set this value if you plan to handle multiply conversation with one contact_identifier simultaneously. Can be a maximum of 255 characters. |
contact | An object containing create or update fields for the contact. For a full reference please see the JSON API documentation for contacts. |
attachments | A list of attachment objects. Each attachment objects must have a body field and can have an optional description .
Userlike will try to fetch the asset and convert it to a preview that can be displayed in the Message Center. A copy of the asset will be stored for later access. The preview supports all common media types. |
Sending outbound messages to your API endpoint
For every message sent by an operator in your Message Center, Userlike sends an HTTP POST request to your API endpoint, which is defined by your Outbound URL.
Include an API-SECURITY-TOKEN header in the POST request and make sure to check the value against the value of your Outbound auth token.
javascript{ "contact": { "city": null, "company": null, "contact_by_email": true, "contact_by_phone": false, "contact_by_userlike_channels": true, "contact_by_userlike_messenger": true, "country": null, "created_at": "2020-05-18T12:03:06.443Z", "email": "jsmith@example.com", "email_verified": false, "external_customer_id": null, "first_message_sent_at": "2020-05-18T12:03:06.752Z", "gender": null, "id": 36, "is_blocked": false, "is_mobile_number_verified": false, "last_message_sent_at": "2020-05-18T12:38:39.405Z", "loc_lat": null, "loc_lon": null, "locale": "en_US", "mobile_number": null, "name": "Contact jsmith@example.com", "needs_register": false, "phone_number": null, "position": null, "salutation": null, "street": null, "url_facebook": null, "url_linkedin": null, "url_profile_picture": null, "url_twitter": null, "verified": false }, "contact_identifier": "custom_id_1234", "conversation_identifier": "9d67e219-14d2-4bcc-8562-be61b41b9f43", "message": { "body": "Hello there!", "conversation_id": 1, "msgid": "1.2.1", "sent_at": "2023-03-17T21:06:26.518Z", "type": "message" }, "operator": { "about_me": "string", "display_name": "David", "id": 1, "job_title": "string", "url_image": "https://userlike-cdn-operators.s3-eu-west-1.amazonaws.com/image.jpg" } }
Sending outbound messages with media attachments to your API endpoint
For every message with a media attachement sent by an operator in your Message Center, Userlike sends an HTTP POST request to your API endpoint, which is defined by your Outbound URL.
Include an API-SECURITY-TOKEN header in the POST request and make sure to check the value against the value of your Outbound auth token.
javascript{ "attachment": { "payload": { "url": "https://userlike-store-media-files.s3.amazonaws.com/3-8fef4c92a8264af1819454684c1ed4c7.jpg" }, "type": "image" }, "contact": { "city": null, "company": null, "contact_by_email": true, "contact_by_phone": false, "contact_by_userlike_channels": true, "contact_by_userlike_messenger": true, "country": null, "created_at": "2020-05-18T12:03:06.443Z", "email": "jsmith@example.com", "email_verified": false, "external_customer_id": null, "first_message_sent_at": "2020-05-18T12:03:06.752Z", "gender": null, "id": 36, "is_blocked": false, "is_mobile_number_verified": false, "last_message_sent_at": "2020-05-18T12:38:39.405Z", "loc_lat": null, "loc_lon": null, "locale": "en_US", "mobile_number": null, "name": "Contact jsmith@example.com", "needs_register": false, "phone_number": null, "position": null, "salutation": null, "street": null, "url_facebook": null, "url_linkedin": null, "url_profile_picture": null, "url_twitter": null, "verified": false }, "contact_identifier": "custom_id_1234", "conversation_identifier": "9d67e219-14d2-4bcc-8562-be61b41b9f43", "message": { "body": "uploaded image", "conversation_id": 71, "msgid": "71.106.347", "sent_at": "2020-05-18T12:53:15.456Z", "title": "20200514_122927.jpg", "type": "upload" }, "operator": { "about_me": "string", "display_name": "David", "id": 1, "job_title": "string", "url_image": "https://userlike-cdn-operators.s3-eu-west-1.amazonaws.com/image.jpg" } }
Python Server Example
We prepared a Python sample server that implements a sample POST request handler and a console-based input client to test the custom channel setup.
Custom Channel V1javascriptimport threading import requests import urllib3 import json import pprint import sys import os import socketserver from http.server import SimpleHTTPRequestHandler urllib3.disable_warnings() CONFIG = { "contact_identifier": "customer_id_1234", "conversation_identifier": "a3238751-9b6f-41f5-b806-5a9dd793d552", "message_uuid": "92fe883c-692e-4e3e-bca0-b71b972c100f", "outbound_url": "http://localhost:8000", "outbound_auth_token": "cub8ulqcdkptd5398hm6r", "inbound_url": "https://api.userlike.com/channel/custom/v2/webhook/?uid=72cf4f88f48b3a7a8bf4d6dc5dbb979129d08c1af34d98a6f181823c486ec99a", "inbound_auth_token": "jtc0l3p2ekj5w5wwm6iuj", } HOST, PORT = CONFIG["outbound_url"].replace("http://", "").split(":") class InboundSimpleHTTPRequestHandler(SimpleHTTPRequestHandler): def do_POST(self): content_length = int(self.headers["Content-Length"]) token = self.headers["API-SECURITY-TOKEN"] body = self.rfile.read(content_length) if token != CONFIG["outbound_auth_token"]: self.send_response(401) self.end_headers() self.wfile.write("Unauthorized") else: self.send_response(200) self.end_headers() self.wfile.write("Ok") data = json.loads(body) print("Output: %s" % pprint.pformat(data)) class ThreadedHTTPServer(object): def __init__(self, host, port, request_handler=SimpleHTTPRequestHandler): socketserver.TCPServer.allow_reuse_address = True self.server = socketserver.TCPServer((host, port), request_handler) self.server_thread = threading.Thread(target=self.server.serve_forever) self.server_thread.daemon = True def __enter__(self): self.start() return self def __exit__(self, type, value, traceback): self.stop() def start(self): self.server_thread.start() def stop(self): self.server.shutdown() self.server.server_close() if __name__ == "__main__": handler = InboundSimpleHTTPRequestHandler with ThreadedHTTPServer(HOST, int(PORT), request_handler=handler) as server: while True: body = input("Input: ") data = { "message": { "body": body, "uuid": CONFIG["message_uuid"], }, "conversation_identifier": CONFIG["conversation_identifier"], "contact_identifier": CONFIG["contact_identifier"], } resp = requests.post( CONFIG["inbound_url"], json=data, headers={"API-SECURITY-TOKEN": CONFIG["inbound_auth_token"]}, verify=False, )