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 their email address. 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 line preview for media links.
Channel setup
Start by creating a custom channel in the channel wizard. 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.
Topic | Description |
Inbound messages | |
Inbound media messages | |
Outbound messages | |
Outbound media messages | |
Examples |
Sending inbound messages to the Userlike API endpoint
To send a message to the custom channel you need to send a JSON paket 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.
The email field in the JSON message describes the identity of your contact. Be aware that this field needs to be verified on your side because it enables users with the same email address to gain access to previous conversations with the contact.
javascript{ "message": { "body": "Hello", "email": "jsmith@example.com" } }
Sending inbound messages with media attachments to the Userlike API endpoint
To send a message with a media attachement to the custom channel you need to send a JSON paket 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.
With the "url" field in the JSON message you can pass a URL to your media file. Userlike will try to fetch the asset and convert it to a preview that can be displayed in the Message Center. Userlike will store a copy of the asset with the conversation for later access. The preview supports all common media types.
javascript{ "attachments": [ { "payload": { "description": "Test Image", "url": "https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg" } } ], "message": { "body": "This is a media upload", "email": "jsmith@example.com" } }
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 }, "conversation": { "accepts_messages": true, "id": 71 }, "message": { "body": "Hello", "conversation": { "id": 71 }, "conversation_id": 71, "display_name": "David Voswinkel", "event": null, "id": "71.106.346", "marked_read_contact": true, "marked_read_operator": true, "msgid": "71.106.346", "name": null, "operator_display_name": "David Voswinkel", "operator_id": 16, "operator_name": null, "part_id": 106, "reference": null, "sender": { "id": 16, "type": "operator" }, "sent_at": "2020-05-18T12:52:46.421Z", "sent_at_time": "12:52:46.421", "state": { "error_code": null, "message": "Received by server", "type": 1 }, "type": "message", "url": null }, "operator": { "about_me": "", "display_name": "David Voswinkel", "id": 16, "job_title": "", "role_name": "Manager", "timezone": "Atlantic/Reykjavik", "timezone_offset": 0, "url_image": "https://userlike-cdn-operators.s3-eu-west-1.amazonaws.com/19ab593a35675412343f7548efc90b8804d3edfd00229de81514b250833f9ecd_80x80.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?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Credential=AKIAIFWASH3NCD2224CQ%2F20200518%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Date=20200518T125315Z&X-Amz-Signature=3af99f511c0bdd64677828ec22fc29f68290ea3270f7ea34e6cbfdfe8ede8f2d" }, "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 }, "conversation": { "accepts_messages": true, "id": 71 }, "message": { "body": "uploaded image", "conversation": { "id": 71 }, "conversation_id": 71, "display_name": "David Voswinkel", "display_type": "image", "embed_code": null, "event": null, "height": null, "id": "71.106.347", "marked_read_contact": true, "marked_read_operator": true, "mime_type": "image/jpeg", "msgid": "71.106.347", "name": null, "operator_display_name": "David Voswinkel", "operator_id": 16, "operator_name": null, "part_id": 106, "reference": null, "sender": { "id": 16, "type": "operator" }, "sender_type": "operator", "sent_at": "2020-05-18T12:53:15.456Z", "sent_at_time": "12:53:15.456", "size": 2844665, "state": { "error_code": null, "message": "Received by server", "type": 1 }, "thumbnail_height": null, "thumbnail_url": "https://www.userlike.com/api/um/media/download/thumbnail-3-8fef4c92a8264af1819454684c1ed4c7.jpg", "thumbnail_url_protected": true, "thumbnail_width": null, "title": "20200514_122927.jpg", "type": "upload", "url": "https://www.userlike.com/api/um/media/download/3-8fef4c92a8264af1819454684c1ed4c7.jpg", "url_protected": true, "width": null }, "operator": { "about_me": "", "display_name": "David Voswinkel", "id": 16, "job_title": "", "role_name": "Manager", "timezone": "Atlantic/Reykjavik", "timezone_offset": 0, "url_image": "https://userlike-cdn-operators.s3-eu-west-1.amazonaws.com/19ab593a35675412343f7548efc90b8804d3edfd00229de81514b250833f9ecd_80x80.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.
javascriptimport threading import requests import urllib3 import json import pprint import sys import os import socketserver from http.server import SimpleHTTPRequestHandler urllib3.disable_warnings() CONFIG = { "email": "jsmith@example.com", "outbound_url": "http://localhost:8000", "outbound_auth_token": "cub8ulqcdkptd5398hm6r", "inbound_url": "https://api.userlike.com/channel/custom?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": {"email": CONFIG["email"], "body": body}} requests.post( CONFIG["inbound_url"], json=data, headers={"API-SECURITY-TOKEN": CONFIG["inbound_auth_token"]}, verify=False, )