Conversation API callbacks
This page covers registering webhooks and listening to callbacks from the Sinch Conversation API.
Testing your solution
Certain webhook test services, like https://webhook.site/, Beeceptor, and https://collect2.com/, can be used when trialing a Conversation API solution.
However, these services should only be used for testing purposes. Using the Conversation API can generate high volumes of callback traffic (including multiple delivery states per message, MO reply messages, and more), which can easily exceed the TPS limits of certain webhook services. These high volumes can result in queuing, latency, and rejections of callbacks, which can negatively impact the performance of your Conversation API solution. Additionally, sensitive information may be included in the callback, including message content and contact information.
Ensure that you use a scalable and secure callback/webhook processor after your initial testing is complete.
Note:
When using https://webhook.site/, note that callback delivery retries will not be performed if the initial webhook dispatch is undeliverable and receives a response other than 200OK
. This constraint only exists for https://webhook.site/ at this time.
Webhook Management
The Conversation API delivers contact messages, delivery receipts for app messages and various notifications through callbacks.
API clients can create fine-grained subscriptions for up-to 5 endpoints (webhooks) per conversation API app through the Sinch Portal or by using the /webhooks
management endpoint.
Each webhook represents a subscription for events defined by a list of triggers. The events are delivered by the Conversation API to the webhook target URL. The callbacks are signed with the webhook secret if such is provided. The signature can be used to verify the authenticity and integrity of the callbacks.
Webhook Triggers
Each webhook can subscribe to one or more of the following triggers:
-
MESSAGE_INBOUND
- subscribe to inbound messages from end users on the underlying channels. -
EVENT_INBOUND
- subscribe to inbound events (for example, composing events) from end users on the underlying channels. -
MESSAGE_DELIVERY
- subscribe to delivery receipts for app messages sent on Conversation API channels. -
MESSAGE_SUBMIT
- subscribe to message submission notifications for app messages sent on Conversation API channels. -
EVENT_DELIVERY
- subscribe to delivery receipts for events sent on Conversation API channels. -
CONVERSATION_START
- subscribe to the conversation start events. -
CONVERSATION_STOP
- subscribe to conversation stop events. -
CONVERSATION_DELETE
- subscribe to conversation deleted events. -
CONTACT_CREATE
- subscribe to contact create events. -
CONTACT_DELETE
- subscribe to contact delete events. -
CONTACT_MERGE
- subscribe to contact merge events. -
CONTACT_UPDATE
- subscribe to contact update events. -
CAPABILITY
- this trigger is used to receive the results of asynchronous capability checks . -
OPT_IN
- subscribe to opt-in events . -
OPT_OUT
- subscribe to opt-out events . -
CONTACT_IDENTITIES_DUPLICATION
- subscribe to notifications about contact identity duplications found during message or event processing. -
CHANNEL_EVENT
- subscribe to channel event notifications. -
RECORD_NOTIFICATION
- subscribe to record notifications. -
UNSUPPORTED
- subscribe to receive channel callbacks that are not natively supported by the Conversation API . -
UNSPECIFIED_TRIGGER
: using this value will cause errors.
Smart Conversations Triggers
Note:
Please note that this functionality is currently available for open beta testing.
In addition to the standard webhooks, you may also configure your solution to subscribe to the SMART_CONVERSATION
or MESSAGE_INBOUND_SMART_CONVERSATION_REDACTION
webhooks. These triggers allows you to subscribe to payloads that:
-
Provide machine learning analyses of inbound messages from end users on the underlying channels (in the case of the
SMART_CONVERSATION
webhook) -
Deliver redacted versions of inbound messages (in the case of the
MESSAGE_INBOUND_SMART_CONVERSATION_REDACTION
webhook)
Smart Conversation trigger
The SMART_CONVERSATION
webhook delivers the results of the machine learning analyses. For information on the structure of the callback, see Smart Conversations callbacks.
Message Inbound Smart Conversation Redaction trigger
The MESSAGE_INBOUND_SMART_CONVERSATION_REDACTION
webhook delivers messages as the MESSAGE_INBOUND
webhook does, but the content of the message has been analyzed and masked (or blocked, in some cases) based on the results of the analysis. For information on the structure of the callback, see Message Inbound Smart Conversation Redaction callback
Creating webhooks using the API
The snippet below demonstrates how to create a webhook using the management API. The webhook registers a POST capable endpoint with URL {{WEBHOOK_URL}}
to receive inbound messages and delivery receipts for app with ID {{APP_ID}}
and project with ID {{PROJECT_ID}}
.
curl -X POST \
'https://eu.conversation.api.sinch.com/v1/projects/{{PROJECT_ID}}/webhooks' \
-H 'Content-Type: application/json' --header 'Authorization: Bearer {{ACCESS_TOKEN}}' \
-d '{
"app_id": "{{APP_ID}}",
"target": "{{WEBHOOK_URL}}",
"target_type": "HTTP",
"triggers": ["MESSAGE_DELIVERY", "MESSAGE_INBOUND"]
}'
Authenticating Callbacks
A callback is a POST
request with a notification made by the Sinch Conversation API to a URI of your choosing. Because the Conversation API is making a request to your system, Sinch will authenticate against your system. Therefore, you must configure the authentication method to be used by Sinch.
In general, the Conversation API can authenticate against your system by using:
-
OAuth2.0
is a standard form of authentication that provides enhanced security through the inclusion of an additional token-provision endpoint. The short-lived access tokens, which are only provided after Sinch successfully authenticates against the token-provision endpoint, reduce the risk of credentials being intercepted. Additionally, client IDs and secrets do not need to be included in calls made to your webhook endpoints; they only need to be included when retrieving a token from the token-provision endpoint.
OAuth2.0 can be utilised by the Conversation API by configuring the corresponding webhook with the requisite client ID, client secret, and the URL to fetch OAuth access token. Note that, because callbacks are made by Sinch to a URI of your choosing (rather than a call made by your system to Sinch's endpoints), the access token must be accepted by your system. Additionally, you (or your OAuth2.0 service provider) will define the client ID and secret that Sinch will use to retrieve tokens. OAuth2.0 is recommended for most production environments.
- Additionally, Conversation API webhooks support HMAC (or hash-based message authentication code). HMAC is a cryptographic authentication technique that uses a hash function and a secret key. HMAC can be configured alongside OAuth2.0. When using HMAC validation, a secret token is included in the call, increasing overall security and allowing for payload integrity validation.
Note:
When configuring callback authentication, we recommend using OAuth2.0 authentication. OAuth2.0 provides more safety through the use of short-lived acess tokens.
More information regarding OAuth2.0 and HMAC are provided below.
OAuth2.0
Achieve more secure webhook authentication by providing OAuth 2.0 access tokens.
Access tokens are short lived. Typically, they will only last one hour. This is done to help keep your data (and ours) safer. This is accomplished, in part, because OAuth2.0 helps ensure that requests arriving at your webhook are from the Conversation API, and not a third-party impersonating the Conversation API.
When exchanging credentials, your system (or the service you use for OAuth2.0 token provision) will provide Sinch with a long string called an access token. This access token will serve as Sinch's bearer token in the authorization header of webhook calls.
When configuring OAuth2.0 for your system, you can either:
- Configure your own OAuth token-provision endpoint, as is done in the example flow we provide .
- Make use of a service that supplies you with an OAuth token-provision endpoint.
Either approach is acceptable as long as the endpoint can receive Sinch's client ID and secret and provide an OAuth token. Note that, regardless of your approach, you must create a corresponding Conversation API webhook and populate it with the client ID, client secret, and token-provision endpoint Sinch is to use to retrieve OAuth tokens.
Making use of an OAuth2.0 provision service
Below is a general guide to setting up an account with an OAuth2.0 provider:
- Choose an OAuth2.0 provider. You may use any provider that successfully and safely provides OAuth access tokens. Create an account with the provider if necessary.
- Select an OAuth2.0 product offering from the provider.
- Configure a new OAuth2.0 application. Ensure you select an application that allows APIs to communicate with one another (i.e., a "machine to machine" application). This will allow the Conversation API to automatically communicate with your OAuth2.0 application.
- Complete the application creation process. This may involve selecting or configuring a token management API, managing permissions, defining domains, establishing client IDs and secrets, reviewing details, and running tests using sample code to ensure the token-provision endpoint is working correctly.
- After the application is created, locate the corresponding client information Sinch will use to authenticate with the token-provision endpoint. This information is required by the Conversation API. This includes the domain/endpoint against which the Conversation API will authenticate and the client ID and secret used to authenticate against the endpoint.
- Create a Conversation API webhook and populate it with the information recorded in the previous step.
Once your webhook is configured, you'll need to ensure your system can receive and parse the payload of the callback.
Sample OAuth2.0 flow
You may implement an OAuth2.0 client credentials flow in the way that best suits your organization. We have provided an example of an OAuth2.0 flow (in Node.js, though the general principals can be applied to any language) below. Note that, in this case, token provision is actually managed by the sample code, rather than a third-party service:
const logger = (req, res, next) => {
console.dir(req.body, { depth: null });
next();
};
import express from 'express';
import bodyParser from 'body-parser';
const app = express();
import jwt from 'jsonwebtoken';
const secret = 'YOUR_OAUTH_secret_string';
const clientId = 'YOUR_client_ID';
const clientSecret = 'YOUR_client_secret';
// Token endpoint. Issues new tokens which Conversation API will use at callbacks for the webhook endpoint:
app.post('/token', (req, res) => {
const auth = req.header('Authorization');
if (!auth?.startsWith('Basic ')) {
res.sendSatus(401);
}
const buff = Buffer.from(auth.slice(6), 'base64');
const text = buff.toString('utf-8');
if (text === `${clientId}:${clientSecret}`) {
const now = Math.floor(Date.now() / 1000);
res.setHeader('Content-Type', 'application/json').status(200).end(JSON.stringify({
access_token: jwt.sign({
iat: now,
exp: now + 3600,
foo: 'bar'
}, secret),
token_type: 'Bearer',
expires_in: 3600,
}));
} else {
res.sendStatus(401);
}
})
// Webhook endpoint:
app.post('/', bodyParser(), logger, (req, res) => {
const token = req.header('Authorization');
if (!token?.startsWith('Bearer ')) {
res.sendStatus(401);
}
try {
jwt.verify(token.slice(7), secret);
// Here you can accept the payload of the callback
res.sendStatus(200);
} catch {
res.sendStatus(401);
}
})
const port = 3000;
app.listen(port, () => {
console.log(`listening on port ${port}`)
})
HMAC
HMAC validation can be used alone or with OAuth2.0. Conversation API callbacks triggered by a registered webhook, with a secret
set, will contain the following headers:
Field | Description |
---|---|
x-sinch-webhook-signature-timestamp | Timestamp, in UTC, of when the signature was computed |
x-sinch-webhook-signature-nonce | A unique nonce that can be used to protect against reply attacks |
x-sinch-webhook-signature-algorithm | The HMAC signature algorithm that was used to compute the signature. For now it's set to HmacSHA256. |
x-sinch-webhook-signature | The signature of the raw HTTP body, the timestamp, and the nonce as computed by the Conversation API. |
The receiver of signed callbacks should execute the code found in the following example, after replacing the commented values in the example with their values:
var crypto = require('crypto')
var secret = 'foo_secret1234' // secret used when creating a webhook; replace with your own value
//actual raw message; replace with your own value
var rawBody = '{"app_id":"","accepted_time":"2021-10-18T17:49:13.813615Z","project_id":"e2df3a34-a71b-4448-9db5-a8d2baad28e4","contact_create_notification":{"contact":{"id":"01FJA8B466Y0R2GNXD78MD9SM1","channel_identities":[{"channel":"SMS","identity":"48123456789","app_id":""}],"display_name":"New Test Contact","email":"new.contact@email.com","external_id":"","metadata":"","language":"EN_US"}},"message_metadata":""}'
var nonce = '01FJA8B4A7BM43YGWSG9GBV067' //x-sinch-webhook-signature-nonce; replace with your own value
var timestamp= 1634579353 //x-sinch-webhook-signature-timestamp; replace with your own value
var signedData = rawBody + '.' + nonce + '.' + timestamp
var signature = crypto.createHmac('sha256', secret).update(signedData).digest('base64')
console.log("calculated-signature : " + signature)
console.log("x-sinch-webhook-signature: 6bpJoRmFoXVjfJIVglMoJzYXxnoxRujzR4k2GOXewOE=")
import base64
import hashlib, hmac
secret = 'foo_secret1234' #secret used when creating a webhook; replace with your own value
#actual raw message; replace with your own value
body = '{"app_id":"","accepted_time":"2021-10-18T17:49:13.813615Z","project_id":"e2df3a34-a71b-4448-9db5-a8d2baad28e4","contact_create_notification":{"contact":{"id":"01FJA8B466Y0R2GNXD78MD9SM1","channel_identities":[{"channel":"SMS","identity":"48123456789","app_id":""}],"display_name":"New Test Contact","email":"new.contact@email.com","external_id":"","metadata":"","language":"EN_US"}},"message_metadata":""}'
nonce = '01FJA8B4A7BM43YGWSG9GBV067' #x-sinch-webhook-signature-nonce; replace with your own value
timestamp= 1634579353 #x-sinch-webhook-signature-timestamp; replace with your own value
signed_data = body + '.' + nonce + '.' + str(timestamp)
digest = hmac.new(secret.encode('utf-8'), signed_data.encode('utf-8'), hashlib.sha256).digest()
print('calculated signature : ' + base64.b64encode(digest).decode())
print('x-sinch-webhook-signature: 6bpJoRmFoXVjfJIVglMoJzYXxnoxRujzR4k2GOXewOE=')
Note:
HMAC signature validation is sensitive to white spaces, so any white space characters included in the message will impact the final digest.
Callback Format
Each callback dispatched by Conversation API has a JSON payload with the following top-level properties:
Field | Type | Description |
---|---|---|
project_id | string | The project ID of the app which has subscribed for the callback. |
app_id | string | Id of the subscribed app. |
accepted_time | ISO 8601 timestamp | Timestamp marking when the channel callback was accepted/received by the Conversation API. |
event_time | ISO 8601 timestamp | Timestamp of the event as provided by the underlying channels. |
message_metadata | string | Context-dependent metadata. Refer to specific callback's documentation for exact information provided. |
correlation_id | string | The value provided in field correlation_id of a send message request. |
channel_metadata | object | Additional metadata that might be provided by a channel. See specific channel's documentation for details. |
Each specific callback type adds additional properties to the payload which are described in detail in the section below.
Inbound Message
This callback delivers contact (end-user) messages to the API clients. The message details are given
in a top level message
field (or message_redaction
in case of redacted messages). It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
id | string | The message ID. |
direction | string | The direction of the message, it's always TO_APP for contact messages. |
contact_message | object | The content of the message. See Contact Message for details. |
channel_identity | object | The identity of the contact in the underlying channel. See Channel Identity for details. |
conversation_id | string | The ID of the conversation this message is part of. Will be empty if processing_mode is DISPATCH . |
contact_id | string | The ID of the contact. Will be empty if processing_mode is DISPATCH . |
metadata | string | Usually, metadata specific to the underlying channel is provided in this field. Refer to the individual channels' documentation for more information (for example, SMS delivery receipts). Note that, for Choice message responses or Reply To messages, this field is populated with the value of the message_metadata field of the corresponding Send message request. |
accept_time | ISO 8601 timestamp | Timestamp marking when the channel callback was received by the Conversation API. |
sender_id | string | Applicable to channels that support sender IDs (for example, SMS, MMS, WhatsApp, and more). The sender ID to which the contact sent the message, if applicable. For example, originator msisdn/short code for SMS and MMS. |
processing_mode | string | The Processing Mode of the message. |
Metadata assigned to the conversation, if any, is provided in the top level message_metadata
string field.
For example, a string representation of the metadata_json
field value of a Create conversation request, or the value assigned to the conversation_metadata
field in a Send message request.
The last value provided in any of those fields is returned, replacing any previous value, if present.
Example of Inbound Message Callback
{
"app_id": "01EB37HMH1M6SV18ABNS3G135H",
"accepted_time": "2020-11-16T08:17:44.993024Z",
"event_time": "2020-11-16T08:17:42.814Z",
"project_id": "c36f3d3d-1523-4edd-ae42-11995557ff61",
"message": {
"id": "01EQ8235TD19N21XQTH12B145D",
"direction": "TO_APP",
"contact_message": {
"text_message": {
"text": "Hi!"
}
},
"channel_identity": {
"channel": "MESSENGER",
"identity": "2742085512340733",
"app_id": "01EB37HMH1M6SV18ABNS3G135H"
},
"conversation_id": "01EQ8172WMDB8008EFT4M30481",
"contact_id": "01EQ4174TGGY5B1VPTPGHW19R0",
"metadata": "",
"accept_time": "2020-11-16T08:17:43.915829Z",
"sender_id": "12039414555",
"processing_mode": "CONVERSATION",
"injected": false
},
"message_metadata": "{\"arbitrary\": \"json object stringify as metadata\" }",
"correlation_id": "correlation-id-1"
}
Redacted Messages
Note:
Please note that Smart Conversations functionality is currently available for open beta testing. This trigger will only function if you have enabled Smart Conversations and at least one of the related features is enabled.
Inbound messages can also be delivered using the MESSAGE_INBOUND_SMART_CONVERSATION_REDACTION
webhook trigger, where the content of the message goes through an A.I. analysis and is redacted if required.
It is possible to use this trigger instead of the MESSAGE_INBOUND
, which will deliver a payload with a message_redaction
field instead of a message
, allowing you to easily differentiate the callbacks:
{ "message": { "contact_message": "..." } }
{ "message_redaction": { "contact_message": "..." } }
For information on the structure of the callback, see Message Inbound Smart Conversation Redaction callback
Contact Message
The table below shows the properties of the contact_message
field in inbound message callbacks:
Field | Type | Description |
---|---|---|
reply_to | string | Optional. Included if the contact message is a response to a previous app message. See Reply To for details. |
It also contains one of the following properties depending on the type of message: text_message
, media_message
, location_message
, choice_response_message
, media_card_message
, fallback_message
, product_response_message
, channel_specific_message
.
All of these properties are JSON objects described in the sections below.
Text Message
Field | Type | Description |
---|---|---|
text | string | The text included in the contact message. |
Media Message
For channels with a "Share Contact" feature, you will receive the MO as a media_message
containing a URL where you can collect a vCard formatted file as a standard media_message
. Standard retention will apply.
Field | Type | Description |
---|---|---|
url | string | The URL of the media. |
Location Message
Field | Type | Description |
---|---|---|
title | string | The title is shown above the location. The title is sometimes clickable. |
coordinates | object | Geo coordinates of the location specified in the message. See Coordinates for details. |
label | string | Label or name for the position. |
Coordinates
Field | Type | Description |
---|---|---|
latitude | float | Latitude of the geographic coordinate. |
longitude | float | Longitude of the geographic coordinate. |
Choice Response Message
The choice response message represents a contact response to a choice message.
Field | Type | Description |
---|---|---|
message_id | string | The message id containing the choice. |
postback_data | string | The postback data if defined in the selected choice. Otherwise the default is message_id_{text, title} |
Media Card Message
Contact Message containing media and caption.
Field | Type | Description |
---|---|---|
url | string | The URL of the media. |
caption | string | Caption for the media, if supported by channel. |
Fallback Message
Fallback message, appears when original contact message can't be handled.
Field | Type | Description |
---|---|---|
reason | object | Fallback reason. See Reason for details. |
raw_message | string | The raw fallback message if provided by the channel. |
Reply To
The Reply To field contains a reference to the message that the corresponding contact message is responding to. This field is only set if the responding contact message was sent within 3 days of the original message. This feature is only supported on the following channels: WhatsApp, Telegram, Messenger, Instagram, and Viber BM.
Field | Type | Description |
---|---|---|
message_id | object | The ID of the message that this contact message is a response to. |
Product Response Message
Field | Type | Description |
---|---|---|
products | object array | The selected products. See Product Item for details. |
text | string | Text that may be sent with the selected products. |
catalog_id | string | The catalog id that the selected products belong to. |
Product Item
Field | Type | Description |
---|---|---|
id | string | The ID for the product. |
marketplace | string | The marketplace to which the product belongs. |
quantity | integer | The quantity of the chosen product. |
item_price | float | The price for one unit of the chosen product. |
currency | string | The currency of the item_price . |
Channel Specific Message
Contact Message containing a message of a channel-specific message type (not supported by OMNI types) from a specific channel.
Field | Type | Description |
---|---|---|
message_type | string | The type of the message. Possible type: nfm_reply |
message | object | The message content. See Interactive Nfm Reply for more details. |
Interactive Nfm Reply
This object represents the message content of a nfm_reply
message type.
Field | Type | Description |
---|---|---|
type | string | The interactive message type. Always nfm_reply . |
nfm_reply | string | The nfm reply message body. See Nfm Reply for more details. |
Nfm Reply
Field | Type | Description |
---|---|---|
name | string | The nfm reply type. Possible types: flow , address_message . |
response_json | string | The JSON specific data. |
body | string | The message body. |
Channel Identity
The table below shows the properties of the channel_identity
field in Conversation API callbacks:
Field | Type | Description |
---|---|---|
channel | string | Conversation API identifier of the underlying channel example, SMS , RCS , MESSENGER . |
identity | string | The channel identity example, a phone number for SMS, WhatsApp and Viber Business. |
app_id | string | The app ID if this is an app-scoped channel identity. Empty string otherwise. See App-scoped Channel Identities for details. |
App-scoped Channel Identities
Currently, Facebook Messenger, Viber Bot, Instagram, Apple Messages for Business, LINE and WeChat channels are using app-scoped channel identities which means contacts will have different channel identities for different apps. For example, Facebook Messenger uses PSIDs (Page-Scoped IDs) as channel identities. The app_id is pointing to the app linked to the Facebook page for which this PSID is issued.
Inbound Event
This callback delivers channel events such as composing to the API clients. The message details are given in a top level event
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
id |
string | The event ID. |
direction |
string | The direction of the event. It's always TO_APP for contact events. |
contact_event |
object | The content of the event. See Contact Event for details. Mutually exclusive with contact_message_event |
contact_message_event |
object | The content of the event when contact_event is not populated. Note that this object is currently only available to select customers for beta testing. Mutually exclusive with contact_event . See Contact Message Event for details. |
channel_identity |
object | The identity of the contact in the underlying channel. See Channel Identity for details. |
contact_id |
string | The ID of the contact. Will be empty if processing_mode is DISPATCH . |
conversation_id |
string | The ID of the conversation this event is part of. Will be empty if processing_mode is DISPATCH . |
accept_time |
ISO 8601 timestamp | Timestamp marking when the channel callback was received by the Conversation API. |
processing_mode |
string | The Processing Mode of the event. |
Example of Inbound Event Callback
Below is an example of an inbound event callback. This is a standard example in which the contact_event
object is populated:
{
"app_id": "01EB37HMH1M6SV18ABNS3G135H",
"accepted_time": "2020-11-16T08:17:44.993024Z",
"event_time": "2020-11-16T08:17:42.814Z",
"project_id": "c36f3d3d-1523-4edd-ae42-11995557ff61",
"event": {
"id": "01GJMQ28NDF6FP0REWQ70N2W3E",
"direction": "TO_APP",
"contact_event": {
"composing_event": {}
},
"channel_identity": {
"channel": "RCS",
"identity": "123456789",
"app_id": ""
},
"contact_id": "01EQ4174TGGY5B1VPTPGHW19R0",
"conversation_id": "01GJMQ3782FWM7TKAZKQZAEF56",
"accept_time": "2020-11-16T08:17:43.915829Z",
"processing_mode": "CONVERSATION"
}
}
Below is an alternative example of the inbound event callback with the contact_message_event
object populated:
{
"app_id": "01EB37HMH1M6SV18ABNS3G135H",
"accepted_time": "2020-11-16T08:17:44.993024Z",
"event_time": "2020-11-16T08:17:42.814Z",
"project_id": "c36f3d3d-1523-4edd-ae42-11995557ff61",
"event": {
"id": "01GJMQ28NDF6FP0REWQ70N2W3E",
"direction": "TO_APP",
"contact_message_event": {
"payment_status_update_event": {
"reference_id": "testing-payment-sep1",
"payment_status": "PAYMENT_STATUS_UNKNOWN",
"payment_transaction_status": "PAYMENT_STATUS_TRANSACTION_SUCCESS",
"payment_transaction_id": "payment-transaction-id1"
}
},
"channel_identity": {
"channel": "RCS",
"identity": "123456789",
"app_id": ""
},
"contact_id": "01EQ4174TGGY5B1VPTPGHW19R0",
"conversation_id": "01GJMQ3782FWM7TKAZKQZAEF56",
"accept_time": "2020-11-16T08:17:43.915829Z",
"processing_mode": "CONVERSATION"
}
}
Contact Event
The table below shows the properties of the contact_event
field in inbound event callbacks:
Field | Type | Description |
---|---|---|
composing_event |
object | Empty object denoting the contact is composing a message. |
comment_event |
object | Object which contains information of a comment made by an user outside of the main conversation context. Currently only supported on Instagram channel, see Instagram Private Replies for more details |
Contact Message Event
Note:
This functionality is currently only available to select customers for beta testing.
The table below shows the properties of the contact_message_event
field contained within inbound event callbacks:
Field | Type | Description |
---|---|---|
payment_status_update_event |
object | Object reflecting the current state of a particular payment flow. See Payment Status Update Event for more details. |
shortlink_activated_event |
object | Object reflecting an event that is created when a contact visits a shortlink. Currently, this is only supported for the Messenger and Instagram channels. See Shortlink Activated Event for more details. |
reaction_event |
object | Object reflecting an event that is created when a contact reacts/unreacts with an emoji to a particular MT message. Currently, this is only supported for the Messenger and Instagram channels. See Reaction Event for more details. |
Payment Status Update Event
Note:
This functionality is currently only available to select customers for beta testing.
The table below shows the properties of the payment_status_update_event
field that is contained within the contact_message_event
object:
Field | Type | Description |
---|---|---|
reference_id |
string | Unique identifier for the corresponding payment of a particular order. |
payment_status |
string | The stage the payment has reached within the payment flow. |
payment_transaction_status |
string | The status of the stage detailed in payment_status . |
payment_transaction_id |
string | Unique identifier of the payment_transaction_status . |
The payment_status
field can have the following values:
-
PAYMENT_STATUS_UNKNOWN
- The status value was not set. Treat it as null or not present field -
PAYMENT_STATUS_NEW
- The user has sent an MT payment message, but the recipient didn’t start a payment yet -
PAYMENT_STATUS_PENDING
- The recipient has started the payment process -
PAYMENT_STATUS_CAPTURED
- The payment was captured -
PAYMENT_STATUS_CANCELED
- The payment was canceled by the recipient and no retry is possible -
PAYMENT_STATUS_FAILED
- The payment attempt failed, but the recipient can retry
The payment_transaction_status
field can have the following values:
-
PAYMENT_STATUS_TRANSACTION_UNKNOWN
- The transaction status value was not set. Treat it as null or not present field -
PAYMENT_STATUS_TRANSACTION_PENDING
- The transaction started -
PAYMENT_STATUS_TRANSACTION_FAILED
- The transaction failed -
PAYMENT_STATUS_TRANSACTION_SUCCESS
- The transaction completed successfully. -
PAYMENT_STATUS_TRANSACTION_CANCELED
- The transaction has been cancelled (by either the recipient or the user/business).
Shortlink Activated Event
Note:
This functionality is currently only available for the Messenger and Instagram channels.
The table below shows the properties of the shortlink_activated_event
field that is contained within the contact_message_event
object:
Field | Type | Description |
---|---|---|
payload |
string | Refers to the payload previously configured to be sent in the postback. |
title |
string | Only relevant for the Instagram channel. |
ref |
string | The ref parameter from the shortlink the user visited. |
source |
string | Defaults to "SHORTLINK" for this type of event. |
source |
string | The source of this referral. For Instagram and Messenger shortlinks, the value of the source is "SHORTLINK". |
type |
string | The identifier for the referral. For Instagram and Messenger shortlinks, this is always set to "OPEN_THREAD". |
existing_thread |
string | Set to true if target channel's conversation thread already existed at the moment the shortlink was visited. Set to false if a new conversation thread began when the shortlink was visited. |
Reaction event
Note:
This functionality is currently only available for the Messenger and Instagram channels.
The table below shows the properties of the reaction_event
field that is contained within the contact_message_event
object:
Field | Type | Description |
---|---|---|
emoji |
string | Indicates that an emoji reaction was placed on a message. This value is the string representation of the emoji. For example: "\u{2764}\u{FE0F}" |
action |
string | Type of action. |
message_id |
string | The ID of the MT message that this reaction is associated with. |
reaction_category |
string | If present, represents the grouping of emojis. Example values: "smile, "angry, "sad, "wow, "love, "like, "dislike, "other" |
The action
field can have the following values:
-
REACTION_ACTION_UNKNOWN
- Unrecognized type of action -
REACTION_ACTION_REACT
- User placed some emoji reaction -
REACTION_ACTION_UNREACT
- User removed previously placed emoji reaction
Message Delivery Receipt
This callback notifies the API clients about status changes of already sent app message. The delivery receipt details are given in a top level message_delivery_report
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
message_id | string | The ID of the app message. |
conversation_id | string | The ID of the conversation the app message is part of. Will be empty if processing_mode is DISPATCH . |
status | string | The delivery status. See Delivery Status for details. |
channel_identity | object | The identity of the contact in the underlying channel. See Channel Identity for details. |
contact_id | string | The ID of the contact. Will be empty if processing_mode is DISPATCH . |
reason | object | Error reason if status is FAILED or SWITCHING_CHANNEL . See Reason for details. |
metadata | string | Metadata specified in the message_metadata field of a Send Message request, if any. |
processing_mode | string | The Processing Mode of the message. |
Additional metadata, if any, is provided in the top level message_metadata
string field.
This is specific to the underlying channel used. Refer to the individual channels' documentation for more information.
For example, SMS delivery receipts.
Note:
Delivery receipts depend on the existence of message mapping, which is metadata that enables matching messages by channelMessageId
.
Dispatching is limited to the lesser value between 30 days or the retention policy specified by App.
Delivery receipts will not be generated after this time. The 30 day restriction is due to Entity Retention Job
, which permanently removes message mappings from the datastore.
Note that a retention cleanup job runs once every twenty-four hours, which can lead to delays between the minute in which message mappings become eligible for deletion and the moment in which the message mappings are actually deleted.
Example of Message Delivery Receipt
The example below shows a receipt for successfully enqueued message with ID 01EQBC1A3BEK731GY4YXEN0C2R
on MESSENGER
channel:
{
"app_id": "01EB37HMH1M6SV18BSNS3G135H",
"accepted_time": "2020-11-17T15:09:11.659Z",
"event_time": "2020-11-17T15:09:13.267185Z",
"project_id": "c36f3d3d-1513-2edd-ae42-11995557ff61",
"message_delivery_report": {
"message_id": "01EQBC1A3BEK731GY4YXEN0C2R",
"conversation_id": "01EPYATA64TMNZ1FV02JKF12JF",
"status": "QUEUED_ON_CHANNEL",
"channel_identity": {
"channel": "MESSENGER",
"identity": "2734085512340733",
"app_id": "01EB27HMH1M6SV18ASNS3G135H"
},
"contact_id": "01EXA07N79THJ20WSN6AS30TMW",
"metadata": "",
"processing_mode": "CONVERSATION"
},
"message_metadata": ""
}
When the sending of the message failed the receipt includes a reason object describing the error:
{
"app_id": "01EB37HMH1M6SV18BSNS3G135H",
"accepted_time": "2020-11-17T16:01:06.374Z",
"event_time": "2020-11-17T16:01:07Z",
"project_id": "c36f3d3d-1513-4edd-ae42-11995557ff61",
"message_delivery_report": {
"message_id": "01EQBF0BT63J7S1FEKJZ0Z08VD",
"conversation_id": "01EQBCFQR3EGE60P42H6H1117J",
"status": "FAILED",
"channel_identity": {
"channel": "WHATSAPP",
"identity": "12345678910",
"app_id": ""
},
"contact_id": "01EXA07N79THJ20WSN6AS30TMW",
"reason": {
"code": "OUTSIDE_ALLOWED_SENDING_WINDOW",
"description": "The underlying channel reported: Message failed to send because more than 24 hours have passed since the customer last replied to this number",
"sub_code": "UNSPECIFIED_SUB_CODE"
},
"metadata": ""
},
"message_metadata": ""
}
Delivery Status
The field status
is included in delivery receipt callbacks and shows the status of the message or event delivery. The status
field can have the following values:
-
QUEUED_ON_CHANNEL
-
DELIVERED
-
READ
-
FAILED
-
SWITCHING_CHANNEL
Each message and event sent by the API clients to contacts go through the following states:
-
A message has the
QUEUED_ON_CHANNEL
status once it is successfully dispatched to an underlying channel. -
A delivery receipt is sent from the underlying channel detailing the delivery status on the channel. Depending on the channel response and the processing state of the message, the message is transitioned to one of following states:
-
DELIVERED
- the channel delivery receipt indicates the message has reached the end user. Some channels can subsequently send new delivery receipts with aREAD
status. -
READ
- the channel delivery receipt indicates the message was seen or read by the end user. This is a terminal state. There are no more state changes after the message reaches theREAD
state. Some channels will omit sending a delivery receipt with aDELIVERED
state when the message is seen immediately by the user. In such cases, theDELIVERED
status is implicit. -
FAILED
- the channel delivery receipt indicates the message delivery failed and there are no more channels to try according to the channel priority defined in the send request. This is a terminal state. There are no more state changes after the message reaches theFAILED
state. -
SWITCHING_CHANNEL
- the channel delivery receipt indicates that message delivery failed. However, there are more channels through which the Conversation API can try to send the message according to the channel priority defined in the send request.
-
Reason
The reason
field in FAILED
or SWITCHING_CHANNEL
delivery receipt callbacks provides information for the reason of the failure.
The table below shows the properties of the reason
field:
Field | Type | Description |
---|---|---|
code | string | High-level classification of the error. See Error Codes for details. |
description | string | A description of the reason. |
sub_code | string | The sub code is a more detailed classification of the main error. See Error Sub-Codes for details. |
Error Codes
Conversation API provides a set of common reason codes which can be used to automate the error handling by the API clients. The codes are as follow:
-
RATE_LIMITED
- the message or event wasn't sent due to rate limiting. -
RECIPIENT_INVALID_CHANNEL_IDENTITY
- the channel recipient identity was malformed. -
RECIPIENT_NOT_REACHABLE
- it wasn't possible to reach the contact, or channel recipient identity, on the channel.Note:
When using the WhatsApp channel, you may receive this error code if you send a marketing template message to a recipient, and the recipient is part of the current WhatsApp marketing message experiment.
-
RECIPIENT_NOT_OPTED_IN
- the contact, or channel recipient identity, hasn't opted in on the channel. -
OUTSIDE_ALLOWED_SENDING_WINDOW
- the allowed sending window has expired. See the channel support documentation for more information about how the sending window works for the different channels. -
CHANNEL_FAILURE
- the channel failed to accept the message. The Conversation API performs multiple retries in case of transient errors. -
CHANNEL_BAD_CONFIGURATION
- the channel configuration of the app is wrong. The bad configuration caused the channel to reject the message. -
CHANNEL_CONFIGURATION_MISSING
- the referenced app has no configuration for the channel. This may be considered an internal error . -
MEDIA_TYPE_UNSUPPORTED
- indicates that the sent message had an unsupported media type. -
MEDIA_TOO_LARGE
- some of the referenced media files are too large. See the channel support documentation to find out the limitations on file size that the different channels impose. -
MEDIA_NOT_REACHABLE
- the provided media link wasn't accessible from the Conversation API or from the underlying channels. Please make sure that the media file is accessible. -
NO_CHANNELS_LEFT
- no channels to try to send the message to. This error will occur if all applicable channels have been attempted. This may be considered an internal error . -
TEMPLATE_NOT_FOUND
- the referenced template wasn't found. This may be considered an internal error . -
TEMPLATE_INSUFFICIENT_PARAMETERS
- not all parameters defined in the template were provided when sending a template message. -
TEMPLATE_NON_EXISTING_LANGUAGE_OR_VERSION
- the selected language, or version, of the referenced template didn't exist. Please check the available versions and languages of the template. -
DELIVERY_TIMED_OUT
- the message or event delivery failed due to a channel-imposed timeout. -
DELIVERY_REJECTED_DUE_TO_POLICY
- the message or event was rejected by the channel due to a policy. Some channels have specific policies that must be met to send a message. See the channel support documentation for more information about when this error will be triggered. -
CONTACT_NOT_FOUND
- the provided Contact ID didn't exist. This can be considered an internal error . -
BAD_REQUEST
- Conversation API validates send requests in two different stages. The first stage is right before the message is enqueued. If this first validation fails the API responds with 400 Bad Request and the request is discarded immediately. The second validation kicks in during message processing and it normally contains channel specific validation rules. Failures during second request validation are delivered as callbacks toMESSAGE_DELIVERY (EVENT_DELIVERY)
webhooks with ReasonCodeBAD_REQUEST
. -
UNKNOWN_APP
- missing app. This error may occur when the app is removed during message processing. This can be considered an internal error . -
NO_CHANNEL_IDENTITY_FOR_CONTACT
- the contact has no channel identities for the resolved channel priorities. This can be considered an internal error . -
NO_PERMISSION
- Check the configuration of the underlying channel (Facebook Messenger or Instagram) to ensure that it has the correct permissions set to be able to access the profile or the page. -
NO_PROFILE_AVAILABLE
- Facebook Messenger profile information cannot be retrieved for accounts created using a phone number. -
UNSUPPORTED_OPERATION
- Generic failure and/or unsupported operation. This can be considered an internal error . Contact Support. -
INACTIVE_CREDENTIAL
- The channel specified has not yet completed provisioning or provisioning has failed. Note that a channel is not usable on a Conversation API app until it enters an "active" state. This can be considered an internal error . -
MESSAGE_EXPIRED
- The TTL has expired for this message. This can be considered an internal error . -
MESSAGE_SPLIT_REQUIRED
- The message exceeds the length limit, or other dimensional constraint(s), of the channel. The message must be split into multiple, smaller segments. This can be considered an internal error . -
DELIVERY_REPORT_TIME_OUT
- This error occurs when a positive delivery report for a message is not received within the specified time. Review the Delivery Report Based Fallback . -
CHANNEL_REJECT
- generic error for channel permanently rejecting a message. -
UNKNOWN
- returned if no other code can be used to describe the encountered error. -
INTERNAL_ERROR
- an internal error occurred. Please save the entire callback if you want to report an error.
Internal errors
Some of the above errors can be categorized as "internal errors". These include:
-
CHANNEL_CONFIGURATION_MISSING
-
NO_CHANNELS_LEFT
-
TEMPLATE_NOT_FOUND
-
CONTACT_NOT_FOUND
-
UNKNOWN_APP
-
NO_CHANNEL_IDENTITY_FOR_CONTACT
-
UNSUPPORTED_OPERATION
-
INACTIVE_CREDENTIAL
-
MESSAGE_EXPIRED
-
MESSAGE_SPLIT_REQUIRED
These errors occur before the message is queued on a particular channel. If you encounter these errors, note that the QUEUED_ON_CHANNEL
status will not be generated for the corresponding message. Other, non-internal errors occur before and after the message reaches the QUEUED_ON_CHANNEL
status.
Error Sub-Codes
The sub_code
field is a more detailed classification of the main error. These sub-codes can differ between channels, and may be supplied by the channel itself.
Note:
If you would like more information on a channel-specific error sub-code, you can search for the error sub-code in the documentation maintained by the corresponding channel.
Below are sub-codes that can correspond to errors associated with messages sent on any channel:
-
UNSPECIFIED_SUB_CODE
- used if no other sub code can be used to describe the encountered error. -
ATTACHMENT_REJECTED
- occurs when the message attachment has been rejected by the channel due to a policy. Some channels have specific policies that must be met to receive an attachment.
Message Submit Notification
This callback provides a notification to the API clients that the corresponding app message was submitted to a channel. This notification is created before any confirmation from Delivery Receipts. The message submission details are given in a top level message_submit_notification
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
message_id | string | The ID of the app message. |
conversation_id | string | The ID of the conversation the app message is part of. Will be empty if processing_mode is DISPATCH . |
channel_identity | object | The identity of the contact in the underlying channel. See Channel Identity for details. |
contact_id | string | The ID of the contact. Will be empty if processing_mode is DISPATCH . |
submitted_message | object | The app message submitted to the channel. |
metadata | string | Metadata specified in the message_metadata field of a Send Message request, if any. |
processing_mode | string | The Processing Mode of the message. |
Additional metadata, if any, is provided in the top level message_metadata
string field.
This is specific to the underlying channel used. Refer to the individual channels' documentation for more information.
For example, SMS delivery receipts.
Example of Message Submit Notification
The example below shows a notification for a submitted message with ID 01EQBC1A3BEK731GY4YXEN0C2R
on the MESSENGER
channel:
{
"app_id":"01EB37HMH1M6SV18BSNS3G135H",
"accepted_time": "2020-11-17T15:09:11.659Z",
"event_time": "2020-11-17T15:09:13.267185Z",
"project_id":"c36f3d3d-1513-2edd-ae42-11995557ff61",
"message_submit_notification":{
"message_id":"01EQBC1A3BEK731GY4YXEN0C2R",
"conversation_id":"01EPYATA64TMNZ1FV02JKF12JF",
"channel_identity":{
"channel":"MESSENGER",
"identity":"2734085512340733",
"app_id":"01EB27HMH1M6SV18ASNS3G135H"
},
"contact_id":"01EXA07N79THJ20WSN6AS30TMW",
"submitted_message":{
"text_message":{
"text":"Hello from Conversation API!"
}
},
"metadata":"",
"processing_mode":"CONVERSATION"
},
"message_metadata":""
}
Event Delivery Receipt
This callback notifies the API clients about status changes of already sent app events. The delivery receipt details are given in a top level event_delivery_report
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
event_id | string | The ID of the app event. |
status | string | The delivery status. See Delivery Status for details. |
channel_identity | object | The identity of the contact in the underlying channel. See Channel Identity for details. |
contact_id | string | The ID of the contact. Will be empty if processing_mode is DISPATCH . |
reason | object | Error reason if status is FAILED or SWITCHING_CHANNEL . See Reason for details. |
metadata | string | Metadata specified when sending the event if any. |
processing_mode | string | The Processing Mode of the event. |
Example of Event Delivery Callback
{
"app_id": "01EB37HMH1M6SV18BSNS3G135H",
"accepted_time": "2020-11-17T15:09:11.659Z",
"event_time": "2020-11-17T15:09:13.267185Z",
"project_id": "c36f3d3d-1513-2edd-ae42-11995557ff61",
"event_delivery_report": {
"event_id": "01EQBC1A3BEK731GY4YXEN0C2R",
"status": "QUEUED_ON_CHANNEL",
"channel_identity": {
"channel": "MESSENGER",
"identity": "2734085512340733",
"app_id": "01EB27HMH1M6SV18ASNS3G135H"
},
"contact_id": "01EXA07N79THJ20WSN6AS30TMW",
"metadata": "",
"processing_mode": "CONVERSATION"
},
"message_metadata": ""
}
Conversation Start
This callback is sent when a new conversation between the subscribed app and a contact is started. The conversation details are given in a top level conversation_start_notification
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
conversation | object | The properties of the started conversation |
Example of Conversation Start Callback
{
"app_id": "01EB37HMH1M6SV18ASNS3G135H",
"project_id": "c36f3d3d-1523-4edd-ae42-11995557ff61",
"conversation_start_notification": {
"conversation": {
"id": "01EQ4174WMDB8008EFT4M30481",
"app_id": "01EB37HMH1M6SF18ASNS3G135H",
"contact_id": "01BQ8174TGGY5B1VPTPGHW19R0",
"active_channel": "MESSENGER",
"active": true,
"metadata": ""
}
}
}
Conversation Stop
This callback is sent when a conversation between the subscribed app and a contact is stopped. The conversation details are given in a top level conversation_stop_notification
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
conversation | object | The properties of the stopped conversation. |
Example of Conversation Stop Callback
{
"app_id": "01EB37HMH1M6SV17ASNS3G135H",
"project_id": "c36f3d3d-1523-4edd-ae42-11995557ff61",
"conversation_stop_notification": {
"conversation": {
"id": "01EPYATZ64TMNZ1FV02JKD12JF",
"app_id": "01EB37HMH1M6SV17ASNS3G135H",
"contact_id": "01EKA07N79THJ20WAN6AS30TMW",
"last_received": "2020-11-17T15:09:12Z",
"active_channel": "MESSENGER",
"active": false,
"metadata": ""
}
}
}
Contact Create
This callback is sent when a new contact is created. The contact details are given in a top level contact_create_notification
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
contact | object | The properties of the created contact. |
Example of Contact Create Callback
{
"app_id": "",
"accepted_time": "2020-11-17T15:36:28.155494Z",
"project_id": "c36f3a3d-1513-4edd-ae42-11995557ff61",
"contact_create_notification": {
"contact": {
"id": "01EQBDK8771J6A1FV8MQPE1XAR",
"channel_identities": [
{
"channel": "VIBER",
"identity": "9KC0p+pi4zPGFO99ACDxdQ==",
"app_id": "01EB37KMH1M6SV18ASNS3G135H"
}
],
"channel_priority": ["VIBER"],
"display_name": "Unknown",
"email": "",
"external_id": "",
"metadata": "",
"language": "UNSPECIFIED"
}
}
}
Contact Delete
This callback is sent when a contact is deleted. The contact details are given in a top level contact_delete_notification
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
contact | object | The properties of the deleted contact. |
Example of Contact Delete Callback
{
"app_id": "",
"accepted_time": "2020-11-17T15:44:33.517073Z",
"project_id": "c36f3a3d-1513-4edd-ae42-11995557ff61",
"contact_delete_notification": {
"contact": {
"id": "01EQBDK8771J6A1FV8MQPE1XAR",
"channel_identities": [
{
"channel": "VIBER",
"identity": "9KC0p+pi4zPGFO99ACDxdQ==",
"app_id": "01EB37HMH1M6SV13ASNS3G135H"
}
],
"channel_priority": ["VIBER"],
"display_name": "Unknown",
"email": "",
"external_id": "",
"metadata": "",
"language": "UNSPECIFIED"
}
}
}
Contact Update
This callback is sent when a contact is updated. The full updated contact details are given in a top level contact_update_notification
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
contact | object | The properties of the updated contact. |
Example of Contact Update Callback
{
"app_id": "",
"accepted_time": "2020-11-17T15:44:33.517073Z",
"project_id": "c36f3a3d-1513-4edd-ae42-11995557ff61",
"contact_update_notification": {
"contact": {
"id": "01EQBDK8771J6A1FV8MQPE1XAR",
"channel_identities": [
{
"channel": "VIBER",
"identity": "9KC0p+pi4zPGFO99ACDxdQ==",
"app_id": "01EB37HMH1M6SV13ASNS3G135H"
}
],
"channel_priority": ["VIBER"],
"display_name": "Unknown",
"email": "",
"external_id": "",
"metadata": "",
"language": "UNSPECIFIED"
}
}
}
Contact Merge
This callback is sent when two contacts are merged. The details of the resulting merged and
deleted contacts are given in a top level contact_merge_notification
field.
It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
preserved_contact | object | The properties of the resulting merged contact. |
deleted_contact | object | The properties of the deleted contact. |
Example of Contact Merge Callback
{
"app_id": "",
"accepted_time": "2020-11-17T15:53:03.457706Z",
"project_id": "c36f3a3d-1513-4edd-ae42-11995557ff61",
"contact_merge_notification": {
"preserved_contact": {
"id": "01EQBECE7Z4XP21359SBKS1526",
"channel_identities": [
{
"channel": "VIBER",
"identity": "9KC0p+pi4zPGFO99ACDxdQ==",
"app_id": "01EB37KMH1M6SV18ASNS3G135H"
}
],
"channel_priority": ["VIBER"],
"display_name": "Unknown",
"email": "",
"external_id": "",
"metadata": "",
"language": "UNSPECIFIED"
},
"deleted_contact": {
"id": "01EQBEH7MNEZQC0881A4WS17K3",
"channel_identities": [
{
"channel": "VIBER",
"identity": "9KC0p+pi4zaGFO99ACDxdQ==",
"app_id": "01EB37KMH1M6SV18ASNS3G135H"
}
],
"channel_priority": ["VIBER"],
"display_name": "Unknown",
"email": "",
"external_id": "",
"metadata": "",
"language": "UNSPECIFIED"
}
}
}
Contact Identities Duplication Notification
This callback is sent when duplicates of channel identities are found between multiple contacts in the contact database during message and event processing.
Note:
Project scoped channel identities are considered duplicates when they have the same channel and identity pair within the same project. App scoped channel identities are considered duplicates when they have the same app ID, channel, and identity combination within the same project.
When a duplicate is identified, you must use the contact endpoint to resolve the issue. This notification points to the contacts which should be deleted, updated, or merged to resolve the duplication issue.
It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
duplicated_identities | list | List which contains information regarding the duplicated identities. Each list entry contains information about the channel on which the duplicate identities were found and the set of contact IDs containing those duplicated channel identities. |
For more information on contact duplication, see our documentation on Conversation API contact management.
Example of Contact Identities Duplication Notification
{
"app_id": "01EB37KMH2M6SV18ASNS3G135H",
"accepted_time": "2022-09-29T09:16:22.544813845Z",
"event_time": "2022-09-29T09:16:22.544813845Z",
"project_id": "c36f3a3d-1513-4edd-ae42-11995557ff61",
"duplicated_contact_identities_notification": {
"duplicated_identities": [
{
"channel": "channel",
"contact_ids": [
"01EKA07N79THJ20ZSN6AS30TMW",
"01EKA07N79THJ20ZSN6AS30TTT"
]
}
]
},
"message_metadata": ""
}
Capability Check
This callback is used to deliver the results of the asynchronous capability checks. The outcome of the capability check is given in a top level capability_notification
field.
It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
request_id |
string | ID generated when submitting the capability request. Can be used to detect duplicates. |
contact_id |
string | The ID of the contact. |
channel |
string | The channel for which the capability lookup was performed. |
identity |
string | The channel identity. For example, a phone number for SMS, WhatsApp, and Viber Business. |
capability_status |
string | Status indicating the recipient's capability on the channel. One of CAPABILITY_FULL , CAPABILITY_PARTIAL , NO_CAPABILITY , or CAPABILITY_UNKNOWN . |
channel_capabilities |
string array | When capability_status is set to CAPABILITY_PARTIAL , this field includes a list of the supported channel-specific capabilities reported by the channel. |
reason |
object | If the capability check failed, this field contains the reason for the error. See Reason for details. |
The possible values for capability_status
are explained below:
-
CAPABILITY_UNKNOWN
: the channel capability for the contact is unknown because the underlying channel has not made this information available. -
CAPABILITY_FULL
: the specified contact supports all the features of the channel. -
CAPABILITY_PARTIAL
: the specified contact supports a subset of the channel features. -
NO_CAPABILITY
: the specified contact supports none of the channel features.
Example of Capability Check Callback
{
"app_id": "01EB37KMH2M6SV18ASNS3G135H",
"accepted_time": "2020-11-17T16:05:51.724083Z",
"project_id": "",
"capability_notification": {
"contact_id": "01EKA07N79THJ20ZSN6AS30TMW",
"identity": "12345678910",
"channel": "WHATSAPP",
"capability_status": "CAPABILITY_FULL",
"request_id": "01EQBF91XWP9PW1J8EWRYZ1GK2"
}
}
Opt-in
This callback is used to deliver opt-in notifications from the channels.
Important:
Opt-in and opt-out callbacks are only supported on Conversation API channels that support opt-in and opt-out notification types (for example, the Viber Business Messages channel). These callbacks are not triggered on unsupported channels. For example, a recipient sending a reply on the SMS channel with the text STOP, UNSUBSCRIBE, etc., will not trigger an opt-out callback.
The opt-in details are given in a top level opt_in_notification
field with the following properties:
Field | Type | Description |
---|---|---|
request_id |
string | ID generated when making an opt-in registration request. Can be used to detect duplicates. |
contact_id |
string | The ID of the contact which is the subject of the opt-in. Will be empty if processing_mode is DISPATCH . |
channel |
string | The channel of the opt-in. |
identity |
string | The channel identity. For example, a phone number for SMS, WhatsApp and Viber Business. |
status |
string | Status of the opt-in registration. One of OPT_IN_SUCCEEDED , OPT_IN_FAILED , or OPT_IN_STATUS_UNSPECIFIED . |
error_details |
object | This field is populated if the opt-in failed. It contains a single string property, description , containing a human-readable error description. |
processing_mode |
string | The Processing Mode of the opt-in. |
The possible values for status
are explained below:
-
OPT_IN_STATUS_UNSPECIFIED
: the underlying channel doesn't support Opt-in. -
OPT_IN_SUCCEEDED
: the Opt-in registration succeeded. -
OPT_IN_FAILED
: the Opt-in registration failed, see reason inerror_details
field.
Example of Opt-in Callback
{
"app_id": "01EB37HMH1M6SV18ASNS3G135H",
"accepted_time": "2021-06-08T07:54:03.165316Z",
"event_time": "2021-06-08T07:54:02.112Z",
"project_id": "",
"opt_in_notification": {
"contact_id": "01EKA07N79THJ20WSN6AS30TMW",
"channel": "VIBERBM",
"identity": "123456789",
"status": "OPT_IN_SUCCEEDED",
"request_id": "01F7N9TEH11X7B15XQ6VBR04G7",
"processing_mode": "CONVERSATION"
}
}
Opt-out
This callback is used to deliver opt-out notifications from the channels.
Important:
Opt-in and opt-out callbacks are only supported on Conversation API channels that support opt-in and opt-out notification types (for example, the Viber Business Messages channel). These callbacks are not triggered on unsupported channels. For example, a recipient sending a reply on the SMS channel with the text STOP, UNSUBSCRIBE, etc., will not trigger an opt-out callback.
The opt-out details are given in a top level opt_out_notification
field with the following properties:
Field | Type | Description |
---|---|---|
request_id |
string | ID generated when making an opt-out registration request. Can be used to detect duplicates. |
contact_id |
string | The ID of the contact which is the subject of the opt-out. Will be empty if processing_mode is DISPATCH . |
channel |
string | The channel of the opt-out. |
identity |
string | The channel identity. For example, a phone number for SMS, WhatsApp and Viber Business. |
status |
string | Status of the opt-out registration. One of OPT_OUT_SUCCEEDED , OPT_OUT_FAILED , or OPT_OUT_STATUS_UNSPECIFIED . |
error_details |
object | This field is populated if the opt-out failed. It contains a single string property, description , containing a human-readable error description. |
processing_mode |
string | The Processing Mode of the opt-out. |
The possible values for status
are explained below:
-
OPT_OUT_STATUS_UNSPECIFIED
: the underlying channel doesn't support Opt-out. -
OPT_OUT_SUCCEEDED
: the Opt-out registration succeeded. -
OPT_OUT_FAILED
: the Opt-out registration failed, see reason inerror_details
Example of Opt-out Callback
{
"app_id": "01EB37HMH1M6SV18ASNS3G135H",
"accepted_time": "2021-06-08T07:54:03.165316Z",
"event_time": "2021-06-08T07:54:02.112Z",
"project_id": "",
"opt_out_notification": {
"contact_id": "01EKA07N79THJ20WSN6AS30TMW",
"channel": "VIBERBM",
"identity": "123456789",
"status": "OPT_OUT_SUCCEEDED",
"request_id": "01F7N9TEH11X7B15XQ6VBR04G7",
"processing_mode": "CONVERSATION"
}
}
Channel Event
This callback is used to deliver notifications regarding channel-specific information and updates. For example, if your are using the WhatsApp channel of the Conversation API, and your quality rating has been changed to GREEN
, a POST
would be made to the CHANNEL_EVENT
webhook.
The event details are given in a top level channel_event
field with the following properties (note that the channel_event
field is under the channel_event_notification
field):
Field | Type | Description |
---|---|---|
channel |
string | The channel for which the notification is providing information. |
event_type |
string | The type of event being reported. |
additional_data |
object | An object containing additional information regarding the event. The contents of the object depend on the channel and the event_type . |
Example of Channel Event Callback
{
"channel": "WHATSAPP",
"event_type": "WHATS_APP_QUALITY_RATING_CHANGED",
"additional_data": {
"quality_rating": "GREEN"
}
}
Unsupported Callback
Some of the callbacks received from the underlying channels might be specific to a single channel or
may not have a proper mapping in Conversation API yet. In such cases the callbacks are forwarded as is
all the way to the API clients subscribed to the UNSUPPORTED
webhook trigger.
The source of the unsupported callback is given in a top level unsupported_callback
field. It's a JSON object with the following properties:
Field | Type | Description |
---|---|---|
channel | string | The channel which is the source of this callback. |
payload | string | Normally a JSON payload as sent by the channel. |
processing_mode | string | The Processing Mode of the callback. |
id | string | The message ID. |
contact_id | string | The ID of the contact. This field is blank if not supported. |
conversation_id | string | The ID of the conversation this message is part of. This field is blank if not supported. |
channel_identity | object | The identity of the contact in the underlying channel. This field is omitted if not supported. See Channel Identity for details. |
Note
The inclusion of the contact_id
, conversation_id
, and channel_identity
fields is dependent on channel integration support. These fields may be omitted if the callback is not associated with a specific channel identity, or if the integration doesn't support them.
Example of Unsupported Callback
Example of an unsupported callback without the contact_id
, conversation_id
, or channel_identity
fields:
{
"app_id": "01EB37HMF1M6SV18ASNS3G135H",
"accepted_time": "2020-11-17T15:17:05.723864Z",
"event_time": "2020-11-17T15:17:05.683253Z",
"project_id": "c36f3a3d-1513-4edd-ae42-11995557ff61",
"unsupported_callback": {
"channel": "MESSENGER",
"payload": "{\"object\":\"page\",\"entry\":[{\"id\":\"107710160841844\",\"time\":1605626225304,\"messaging\":[{\"sender\":{\"id\":\"107710160841844\"},\"recipient\":{\"id\":\"2744085512340733\"},\"timestamp\":1605626225228,\"message\":{\"mid\":\"m_CE0r2yTXOPe1DG_cepCqA9eapH28NipZLg3HuKNf-MA_Edr55tBwvLiTzhrvjZJWQioE4J4gpETQZBTREc7Hdg\",\"is_echo\":true,\"text\":\"Text message from Sinch Conversation API.\",\"app_id\":477442439539985}}]}]}",
"id": "01FMAVK07YN3SP1B43FP9D1C0S",
"contact_id": "",
"conversation_id": "",
"processing_mode": "CONVERSATION"
}
}
Example of an unsupported callback with the contact_id
, conversation_id
, and channel_identity
fields:
{
"app_id": "01EB37HMF1M6SV18ASNS3G135H",
"accepted_time": "2021-11-12T19:53:54.728511Z",
"event_time": "2021-11-12T19:53:54.542308Z",
"project_id": "c36f3a3d-1513-4edd-ae42-11995557ff61",
"unsupported_callback": {
"channel": "APPLEBC",
"payload": "{\"interactiveData\": ... }",
"id": "01FMAVDCKE8TNN021VN7XQ1VG2",
"contact_id": "01FMAVAPAQTEGDJSFJJWANRX38",
"conversation_id": "01FMAVAQBTR4C1HJZS05PVTXZ8",
"channel_identity": {
"channel": "APPLEBC",
"identity": "urn:mbid:AQAAX12RO5I+XVVAhq2b0S22ohAOSfSB+aZkybPFk6iOaMdgrXXstADziEHz/f/x8cA=",
"app_id": "01EB37HMF1M6SV18ASNS3G135H"
},
"processing_mode": "CONVERSATION"
},
"message_metadata": ""
}