App Registration
To register your app in our system, prepare and provide the following information ⬇️
{
"code": "my_custom_app_code",// unique code for your integration
"callback_url": "https://my_custom_app_host", // URL where requests for app install and uninstall will be sent (we add /portals/install and /portals/uinstall to the callback_url for these actions)
"price": "true", // whether your integration will be paid
"sns_subscription_url": null, // URL where messages from your external lines will be sent
"localized_data": [
{
"locale": "en-US",
"name": "App name",
"logo": "https://...", // link to the logo. We can store the logo on our side, its size should be 240x240 px
"short_description": "Short app description to be displayed on the marketplace page",
"description": "<div class="app-description"><div id="slider"><div class="slides"><img src="https://uspacy.github.io/static/unitalk-slider1.png" width="100%" /></div><div class="slides"><img src="https://uspacy.github.io/static/unitalk-slider2.png" width="100%" /></div><div class="slides"><img src="https://uspacy.github.io/static/binotel-slider3.png" width="100%" /></div><div class="slides"><img src="https://uspacy.github.io/static/unitalk-slider4.png" width="100%" /></div><div class="slides"><img src="https://uspacy.github.io/static/unitalk-slider5.png" width="100%" /> </div> </div>
<h5 class="title">
Expand the capabilities of your CRM by connecting it to the UniTalk IP-telephony.
</h5>
<p class="paragraph">
One of the keys to successful sales is meeting the needs of your clients. And using IP-telephony allows you to establish communication with them and, as a result, significantly improve the level of service.
</p>
<br />
<p class="paragraph">
To make calls to clients, you don't need to install additional third-party applications on your devices. Thanks to a special extension in Google Chrome, you can make incoming and outgoing calls on any browser pages, taking into account your Uspacy Space.
</p>
<img src="https://uspacy.github.io/static/unitalk-slider3.png" alt="unitalk" />
<p class="paragraph">
That is, you don't need to minimize open entity cards in the CRM to dial a client's number or launch a call campaign. The extension can be placed on top of the page and work in parallel with the client or deal database.
</p>
</div>"
}
],
"developer_name": "Uspacy", // Name of your company
"developer_link": "https://uspacy.com/" // Link to your company's website
}
💡Note: You can provide descriptions and names in multiple languages for better localization. Currently, we support the following languages ⬇️
Locale | Language |
uk | Ukraine |
en-US | English US |
pt-BR | Portuguese |
pl | Polish |
es-ES | Spanish |
After providing this information, you will receive a client_id and secret_id for further development.
Backend Development
We have prepared a Laravel PHP template with basic settings. Review the link
🔍 Review the link ➡️ HERE
App Installation
When a user installs the app, you will receive the following request:
POST {callback_url}/portals/install
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiry_date": 1727072924
}
❗️We use a JWT token. To determine the portal that is installing the app, you need to parse the token and verify its authenticity. An example implementation of this logic can be found ➡️ HERE
You need to save:
token – valid for 1 day
refresh_token – valid for 30 days
expiry_date – to check the token's validity
It is recommended to create a cron job to update portal tokens.
🔍 An example can be found ➡️ HERE
App Uninstallation
When the app is uninstalled, you will receive:
DELETE {callback_url}/portals/uninstall
Authorization: Bearer JWT_TOKEN
💡 The token is in the Authorization header. You need to parse the token, get the domain value, and verify the token's authenticity.
Working with External Lines (EL)
Creating external line
To send messages to external lines (EL), you first need to create them. Typically, one connection has one EL. For example, in the Instagram app, the user can add multiple accounts (connections), and we create a separate EL for each of them.
POST https://{domain}.uspacy.ua/messenger/v1/external-lines
Content-Type: application/json
{
"name": "...", // unique name
"icon": "https://...", // link to the icon, this image will be displayed in the bottom right corner of the EL tab
"phoneNumber": "...", // if possible, add a number, if not, you can duplicate the value from the name field
"externalId": "..." // unique EL identifier
}
Save the identifier of the created EL, as you will need it later.
🔍 More information about EL interaction methods can be found ➡️ HERE
Creating a Chat
Next, you need to create a chat. If, for example, webhooks will be receiving events about messages, then having the identifier of the user sending these messages, you can create a chat. Then, in subsequent similar events, you will know which chat to send the processed messages from your service to the EL.
POST https://{domain}.uspacy.ua/messenger/v1/chats
Content-Type: application/json
{
"name": "...",
"type": "EXTERNAL",
"id": "...",
"externalLines": [], // add the EL ID you created earlier here
"pictureUrl": "https://...",
"meta": [ // used to link the chat to the CRM
{
"type": "phone", // options: phone/widgetUser/username/facebook/instagram/telegram/twitter/viber/whatsapp
"value": "..." // you can add a phone number or ID
}
]
}
Save the identifier of the created chat.
These are the basic required fields.
🔍 More information about chat interaction can be found ➡️ HERE
Sending Messages
If your message will contain attachments, you need to upload them first.
Uploading files (if needed):
POST https://{domain}.uspacy.ua/files/v1/files
Content-Type: multipart/form-data
{
"entityType": "chat_message",
"entity_id": "...", // message identifier
"files": []
}
🔍 More information about file storage can be found ➡️ ТУТ
2. Sending a message:
POST https://{domain}.uspacy.ua/messenger/v1/messages
Content-Type: application/json
{
"id": "...",
"externalLine": "",
"chatId": "",
"type": "MESSAGE", // more about types in the documentation
"message": "Message ...",
"externalId": "...",
"externalAuthorId": "...", // ID of the message sender
"parentMessage": "...", // ID of the parent messag
"attachedFiles": [ // data of the uploaded files from the previous step
{
"id": "...",
"entityId": "...",
"lastModified": 3434434343,
"originalFilename": "...",
"size": "...",
"url": "..."
}
]
}
💡 You can use Markdown to format the message text.
🔍 More information about message manipulation can be found ➡️ HERE
3. Receiving a message event:
When you registered the app, you provided the sns_subscription_url field. We will send a webhook about the message in the EL to this URL. You need to check the event that comes in and extract only the payload. For example, you can use the nipwaayoni/laravel-aws-sns package for PHP and add the following code to the controller:
$message = Message::fromJsonString($request->getContent());
$snsMessage = new SnsMessage($message);
$messageBody = json_decode($snsMessage->content(), true);
Here is an example of a received message event with an image:
{
"data": {
"action": "create",
"domain": "demo.uspacy.com",
"entity": {
"attachedFiles": [
{
"creatorId": 3,
"entityId": "37741d88-4903-4cee-b363-6404156c6d82",
"entityType": "chat_message",
"id": 467,
"lastModified": 1727256812,
"originalFilename": "Untitled.jpg",
"size": 5422,
"uploadId": "e3eb7705-8cb8-....",
"url": "https://..."
}
],
"authorId": 3937,
"chatId": "89cc8fc3-ec6c-42ca-9310...",
"externalLine": {
"externalId": "8b64d20c-e616-4e2f...",
"icon": "https://...",
"id": "66eaf2c703bd0...",
"name": "Антон Чернов",
"phoneNumber": "Антон Чернов",
"portal": "test.uspacy.com",
"timestamp": 1726673607915
},
"id": "37741d88-4903-4cee-b363-...",
"mentioned": null,
"message": "Тестове повідомлення",
"parent": {
"active": true,
"externalLines": [
{
"externalId": "8b64d20c-e616-4e2f-b841-...",
"icon": "https://...",
"id": "66eaf2c703bd0d42...",
"name": "Антон Чернов",
"phoneNumber": "Антон Чернов",
"portal": "test.uspacy.com",
"timestamp": 1726673607915
}
],
"id": "89cc8fc3-ec6c-42ca-9310-...",
"lastMessage": {
"attachedFiles": [
{
"creatorId": 3,
"entityId": "37741d88-4903-4cee-b363-...",
"entityType": "chat_message",
"id": 467,
"lastModified": 1727256812,
"originalFilename": "Untitled.jpg",
"size": 5422,
"uploadId": "e3eb7705-8cb8-46e2-900b-...",
"url": "https://..."
}
],
"authorId": 3937,
"chatId": "89cc8fc3-ec6c-42ca-9310-...",
"externalLine": "66eaf2c703bd0d420...",
"id": "37741d88-4903-4cee-b363-...",
"mentioned": null,
"message": "Тестове повідомлення",
"readBy": null,
"relations": null,
"timestamp": 1727256825340,
"type": "MESSAGE"
},
"members": [
3937
],
"meta": [
{
"type": "instagram",
"value": "416955767636055"
}
],
"name": "Антон Чернов",
"ownerId": 3934,
"pictureUrl": "https://...",
"portals": [
"test.uspacy.com"
],
"timestamp": 1727256825340,
"type": "EXTERNAL"
},
"readBy": null,
"relations": null,
"timestamp": 1727256825340,
"type": "MESSAGE"
},
"service": "messenger",
"timestamp": "2024-09-25T09:33:45.390Z",
"user_id": 3
},
"env": "com",
"metadata": null,
"request_id": "c627affe-7a73-449c-baaf-...",
"topic": "Notifications",
"type": "message"
}
After that, we recommend checking that it is a message, and that it is from the EL:
if($messageBody['type'] === 'message' &&
isset($message['data']['entity']['externalLine']))
Next, you can find the corresponding chat in your system by the chatId identifier.