Implementing messaging app extension

This page will cover the technical aspects for adding and creating a manifest and the implementation flows for messaging app extension. For a general overview and development steps, see messaging app extension page and for error codes error codes and troubleshooting page.

Manifest for messaging app extension


A manifest describes, in a special contract format, the endpoints to which Pipedrive will make requests to. Based on the responses to those requests, the data received will be displayed inside Pipedrive UI in a pre-defined format. For the messaging app extension, the data received from the endpoints will be displayed in the Messaging inbox under integrations.

The manifest for your app will need the appropriate URLs of a messaging service’s API endpoints. Once the manifest for your messaging app is submitted to Developer Hub, it will be validated against a pre-defined schema.

Template for messaging manifest

The file below defines the manifest’s JSON schema and the endpoints that the app needs to expose to display the service’s contents inside Pipedrive’s Messaging inbox. All endpoints’ URLs contain the provider_channel_id value, which is the identifier the app gives Pipedrive. Pipedrive uses this ID to make requests to the integration, specifically to fetch data about channels.

{
    "version": "v202101",
    "endpoints": {
        "getConversations": "https://example.com/api/channels/:providerChannelId/conversations",
        "getConversationById": "https://example.com/api/channels/:providerChannelId/conversations/:sourceConversationId",
        "postMessage": "https://example.com/api/channels/:providerChannelId/messages",
        "getSenderById": "https://example.com/api/channels/:providerChannelId/senders/:senderId",
        "deleteChannelById": "https://example.com/api/channels/:providerChannelId",
        "getTemplates": "https://example.com/api/channels/:providerChannelId/templates",
        "getMessageById": "https://example.com/api/channels/:providerChannelId/conversations/:sourceConversationId/messages/:sourceMessageId"
    }
}

From this manifest, some endpoints are optional:

📘

You can import this .yaml file to any API testing tool(e.g., https://editor.swagger.io/) to see exact structures and requirements for requests and responses of Manifest endpoints.


Authentication of the request from Pipedrive


All requests from Pipedrive to the app will be supplemented with an authentication header that uses the same client_id and client_secret as the OAuth token exchange in OAuth authorization.
E.g. Authorization: Basic <base64(client_id:client_secret)>



Pagination cursors


Due to how popular business chat providers work, the Channels API uses a different type of pagination from the standard one used in Pipedrive'd public API.

The pagination for the manifest works with cursors, which are references the application provides to our API, that are sent back to the app when a new page needs to be fetched. For example, in the endpoint getConversations, it’s expected by Pipedrive to receive a property next_messages_cursor, which we will store and send back when our API calls the getConversationById endpoint (after query parameter).

In the same getConversations endpoint, inside the additional_data object, the application will send the after property to our API, which is a cursor that can be used to fetch more conversations in the same endpoint (after query parameter).



Implementation flows


Installation flow

The installation is considered complete when the app is installed via OAuth 2.0 and at least one channel is registered for the installation. The installation process will call the following manifest endpoints during the process:

Manifest endpointRequired to implement?
getConversationsYes
getTemplatesYes, if the channel was registered with the option template_support as true
getSenderByIdYes, if a sender was returned in a conversation for the getConversations request, with the fetch_avatar flag set as true
Channels API endpointUsage required?
POST /channelsThe app needs to register a channel in order to use the extension

818

Sending a message flow

The user can send and receive messages by using a channel. These messages may contain attachments. When a user sends a message, Pipedrive will call the following manifest endpoint:

Manifest endpointRequired to implement?
postMessageYes

The public endpoint to receive new messages will update a message if it already exists in the API.

Channels API endpointRequired to implement?
POST /channels/messages/receiveYes

741

Receiving a message flow

When a channel receives a new message, be it to start a new conversation or to add it to an existing one, that message should be sent to Channels API. When it’s a message related to a new conversation, our API will request more detailed data about the conversation by making a request to the manifest endpoint getConversationById.

Manifest endpointRequired to implement?
getConversationByIdIf the message received does not have a conversation in the API, Channels API will request more details about the conversation with the specified conversation_id
Channels API endpointRequired to implement?
POST /channels/messages/receiveWhen a message from the end-user is received, use this endpoint to register it


Handling expiring links flow

Handling expiring attachments

When an attachment was registered with the option link_expires true, Pipedrive will test the attachment links provided, by sending a HEAD request. In the case that the link is no longer accessible, Pipedrive will make a request to the following manifest endpoint:

Manifest endpointRequired to implement?
getMessageByIdIf the attachment was registered with the option link_expires, this endpoint should be available

Handling expiring avatars

When a sender was registered with the option avatar_expires, Pipedrive will test the avatar link provided by sending a HEAD request (not more often than once per day). If the link is no longer accessible, Pipedrive will request the following manifest endpoint:

Manifest endpointRequired to implement?
getSenderByIdIf the sender was registered with the option avatar_expires, this endpoint should be available

The rest of the update flow will go similarly to the attachment links.




Messaging endpoints for manifest



GET "getConversations" endpoint


Required

The getConversations endpoint is used to sync an initial batch of conversations and their messages. It is called right after a channel is registered in the Channels API using the POST /channels endpoint in the Pipedrive API.

The path parameters sent in the getConversations endpoint:

NameTypeRequired/OptionalDescription
providerChannelIdstringRequired in the manifet's path descriptionA unique providerChannelId that identifies the channel

The query parameters sent in the getConversations endpoint:

NameTypeDescription
conversations_limitintegerThe maximum number of conversations expected in the response
messages_limitintegerThe maximum number of messages expected for each conversation in the response
afterstringA cursor that will be used to fetch more conversations

Expected response from the getConversations endpoint:

{
  "success": true,
  "data": [
    {
      "id": "string",
      "link": "string",
      "status": "open",
      "seen": true,
      "next_messages_cursor": "string",
      "messages": [
        {
          "id": "string",
          "status": "sent",
          "created_at": "2022-03-29T06:38:06.913Z",
          "message": "string",
          "sender_id": "string",
          "reply_by": "2022-03-29T06:38:06.913Z",
          "attachments": [
            {
              "id": "string",
              "type": "string",
              "name": "string",
              "size": 0,
              "url": "string",
              "preview_url": "string"
            }
          ]
        }
      ],
      "participants": [
        {
          "id": "string",
          "name": "string",
          "role": "end_user",
          "avatar_url": "string"
        },
        {
          "id": "string",
          "name": "string",
          "role": "source_user",
          "avatar_url": "string"
        }
      ],
  "additional_data": {
    "after": "string"
  }
}

GET "getConversationById" endpoint


Required

The getConversationById endpoint is used to fetch a specific conversation and its messages. It is called when an incoming message is part of a new conversation and when paginating over messages.

The path parameters sent in the getConversationById endpoint:

NameTypeRequired/OptionalDescription
providerChannelIdstringRequired in the manifest's path descriptionA unique providerChannelId that identifies the channel
sourceConversationIdstringRequired in the manifest's path descriptionThe ID of the conversation, as in the field conversation_id on its messages

The query parameters sent in the getConversationById endpoint:

NameTypeDescription
afterstringThe cursors used for fetching more messages from the provider
messages_limitintegerThe maximum number of messages expected for each conversation in the response.

It's recommended to use the default value (30) and support pagination, for better performance.

Expected response from the getConversationById endpoint:

{
  "success": true,
  "data": {
    "id": "string",
    "link": "string",
    "status": "open",
    "seen": true,
    "next_messages_cursor": "string",
    "messages": [
      {
        "id": "string",
        "status": "sent",
        "created_at": "2022-03-29T06:39:23.251Z",
        "message": "string",
        "sender_id": "string",
        "reply_by": "2022-03-29T06:39:23.251Z",
        "attachments": [
          {
            "id": "string",
            "type": "string",
            "name": "string",
            "size": 0,
            "url": "string",
            "preview_url": "string"
          }
        ]
      }
    ],
    "participants": [
        {
          "id": "string",
          "name": "string",
          "role": "end_user",
          "avatar_url": "string"
        },
        {
          "id": "string",
          "name": "string",
          "role": "source_user",
          "avatar_url": "string"
        }
      ],
  "additional_data": {
    "after": "string"
  }
}

POST "postMessage" endpoint


Required

The postMessage endpoint is used to send a new message to a conversation. It’s called when a user types a message in the chat window and sends it. The endpoint supports messages with file attachments.

The Content-Type header

Unlike the other endpoints, requests for this one will be sent as multipart/form-data, since messages may include file attachments.

The path parameters sent in the postMessage endpoint:

NameTypeRequired/optionalDescription
providerChannelIdstringRequired in the manifest's path descriptionA unique providerChannelId that identifies the channel

The body of the request made to the postMessage endpoint has the following form fields:

NameTypeDescription
messagestringThe name of the activity
senderIdstringThe ID of the user who sent the message
conversationIdstringThe ID of the conversation
recipientIds[]stringIds of all recipients for a message, split by ",". E.g., josef123456,Eva879542
filesbinariesA list of binary files. All files sent with the message in multipart/form-data format (with filename and contentType).

Expected response from the postMessage endpoint:

{
  "success": true,
  "data": {
    "id": "string"
  }
}


GET "getSenderById" endpoint


Optional

Inside the Messaging inbox, messages are displayed with their senders. This endpoint is used to fetch additional and up-to-date information about a sender every time a conversation is updated and available in the service/application.
This endpoint is called occasionally (max once per day) to refresh the sender's avatar if avatar_expires is true and also when there is no avatar for the sender but fetch_avatar is set to true during the sender creation (only once when installing the app).

The path parameters sent in the getSenderById endpoint:

NameTypeRequired/OptionalDescription
providerChannelIdstringRequiredA unique providerChannelId that identifies the channel
senderIdstringRequiredThe senderId as specified in the messages

Expected response from the getSenderById endpoint:

{
  "success": true,
  "data": {
    "id": "string",
    "name": "string",
    "avatar_url": "string"
  }
}


DELETE "deleteChannelById" endpoint


Optional

In some messaging providers (e.g., Facebook Business with various pages), support for multiple channels is provided in the same account. The messaging app extension provides a way to delete channels from the messaging services with this endpoint.

The path parameters sent in the deleteChannelById endpoint:

NameTypeRequired/optionalDescription
providerChannelIdstringRequired in the manifest path descriptionA unique providerChannelId that identifies the channel

Expected response from the deleteChannelById endpoint:

{
  "success": true,
}


GET "getTemplates" endpoint


Optional

The getTemplates endpoint is used to fetch a list of templates, when available in the provider's service. Right now, only approved WhatsApp templates are supported.

1546

WhatsApp templates synced to Messaging inbox

The path parameter sent in the getTemplates endpoint:

NameTypeRequired/OptionalDescription
providerChannelIdstringRequired in the manifest's path descriptionA unique providerChannelId that identifies the channel

Expected response from the getTemplates endpoint:

{
  "success": true,
  "data": {
    "templates": [
      {
        "id": "string",
        "name": "string",
        "content": "string",
        "language": "string"
      }
    ]
  }
}


GET "getMessageById" endpoint


Optional

The getMessageById endpoint is used to get updated messages with attachments. It is used for refreshing an expired attachment's URL when link_expires is set true.

The path parameters sent in the getMessageById endpoint:

NameTypeRequired/OptionalDescription
providerChannelIdstringRequired in the manifest's path descriptionA unique providerChannelId that identifies the channel
sourceMessageIdstringRequired in the manifest's path descriptionThe message ID as specified in the message

Expected response from the getMessageById endpoint:

{
  "id": "string",
  "status": "sent",
  "created_at": "2022-03-29T06:51:33.727Z",
  "message": "string",
  "sender_id": "string",
  "reply_by": "2022-03-29T06:51:33.727Z",
  "attachments": [
    {
      "id": "string",
      "type": "string",
      "name": "string",
      "size": 0,
      "url": "string",
      "preview_url": "string"
    }
  ]
}