# Message status and interactions

Messages go through a life-cycle when being processed; life-cycle events provide an indication of the current status a message has;  `sending_failed` or `delivered` are both examples of messages' status events.&#x20;

Once a message is successfully delivered to its intended receiver, it may (based on the service and condition) generate associated interactions; `opened` and `reported-as-spam` are both examples of message interactions.&#x20;

## Message Lifecycle Events

You can create a webhook subscription to listen to channel message events (see API details [here](https://docs.messagebird.com/api/notifications-api/api-reference/webhooks/create-a-webhook-subscription))&#x20;

Channels supports two message lifecycle event webhook types:

* \<channel>.inbound
* \<channel>.outbound

### \<channel>.inbound

This event is used to receive incoming messages.&#x20;

The message status can be:

* `delivered`
  * When an **incoming** message is created with status `delivered`

The message direction will be:

* `incoming`

This event is available for the following channels:

* `sms`
* `whatsapp`
* `email`
* `line`
* `instagram (including comments and mentions)`
* `facebook`
* `line`
* `viber`
* `linkedin`
* `tiktok`
* `apple business chat`
* `telegram`

**Filter channel ID**

By default, when creating a subscription without specifying any filter, you are going to receive all incoming messages for all channels of that platform e.g WhatsApp

Is it possible to add filters to only receive events for a specific channel ID .

For each subscription, it is only possible to add one channelId filter.

**Example event**

```json
{
  "service": "channels",
  "event": "<channel>.inbound",
  "url": "http://site",
  "signingKey": "key",
  "eventFilters": [
    {
      "key": "channelId",
      "value": "<id>"
    }
  ]
}

```

| Property        | Description                                                                                                                                                                                               | Example value                            |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| service         | The MEP service which generates the event                                                                                                                                                                 | channels                                 |
| event           | The specific event you are subscribing too. For channels events are scoped to all channels for a specific platform e.g. WhatsApp                                                                          | whatsapp.inbound                         |
| url             | The webhook endpoint                                                                                                                                                                                      | <https://site>                           |
| signingKey      | A value that will be used to validate a webhook                                                                                                                                                           | key                                      |
| eventFilters\[] | Event filters are inclusive, which means you will only get events for filters you add. If you do not add a filter you will get all events (except where you have other webhooks with an explicit filter). | { "key": "channelId", "value": "\<id>" } |

**Example payload**

```json
{
  "service": "channels",
  "event": "whatsapp.inbound",
  "payload": {
    "id": "404544c5-9920-45f1-8990-0855634ab7ac",
    "channelId": "52ad151f-f7b4-46f9-a5c6-d3318e650c84",
    "sender": {
      "contact": {
        "id": "d4e8935a-5f48-45a5-95a5-ee7ba1e2c5b4",
        "identifierKey": "phonenumber",
        "identifierValue": "+3511111111"
      }
    },
    "receiver": {
      "connector": {
        "id": "60bf59d6-fc6f-4511-89c4-75d9127d7a7c",
        "identifierValue": ""
      }
    },
    "body": {
      "type": "text",
      "text": {
        "text": "Test"
      }
    },
    "meta": {
      "extraInformation": {
        "timestamp": "1702496037"
      }
    },
    "reference": "",
    "parts": [
      {
        "platformReference": "wamid.HBgMMzUxOTE0MjYyNTM1FQIAEhggQUMzODQyNUY1MDlDQkU1QjJGNTM3RDlENjg0OTJGMDgA"
      }
    ],
    "status": "delivered",
    "reason": "",
    "direction": "incoming",
    "lastStatusAt": "2023-12-13T19:34:01.858Z",
    "createdAt": "2023-12-13T19:34:01.858Z",
    "updatedAt": "2023-12-13T19:34:02.063Z"
  }
}

```

### \<channel>.outbound

This event is used to receive status updates for outgoing messages. The following statuses are currently supported.&#x20;

The message status can be:

* `accepted`
  * When an **outgoing** message has been `accepted` by the channels, API and will be processed by Bird

* `processing`

  * When an **outgoing** is being processed by Bird channels

* `schedued`
  * When an **outgoing** message is being held, waiting for its scheduled time to be sent&#x20;

* `skipped`
  * When an **outgoing** message didn't attempt to send as it was addressed to a suppressed contact&#x20;

* `sent`
  * When an **outgoing** message changes status, it `sent` means it was processed successfully and handed to a downstream entity or carrier for delivery. &#x20;

{% hint style="info" %}
`sent` is a temporary status that will be updated to either final failure or delivery once carrier-generated events are received and processed. Many carriers process status reports asynchronously, so while a message marked as SENT may have already been delivered, the delivery confirmation hasn't yet been received from the downstream carrier."
{% endhint %}

{% hint style="info" %}
Email messages with multiple recipients (e.g. one recipient in "To" and another in "Bcc") have `sent` as a final status, otherwise it'd cause ambiguity if it gets successfully delivered to one recipient but not the other.
{% endhint %}

* `sending_failed`
  * When an **outgoing** message changes status to `sending_failed.` This indicates a failure in the Bird platform, and the message was never submitted to a downstream carrier for delivery. &#x20;
* `delivered`
  * When an **outgoing** message changes status to `delivered.` Delivered is a final status and indicates a message has reached its final destination. &#x20;

{% hint style="info" %}
Downstream carriers handle delivery reports differently. A `DELIVERED`  status might mean the message reached the final recipient or just the last delivery network. Not all platforms or carriers provide reports for delivery to the final recipient; final network reports are standard. In such cases, the message could still be blocked or dropped during the "last mile" to the recipient.
{% endhint %}

* `delivery_failed`
  * When an **outgoing** message changes status to `delivery_failed` following a failure to deliver downstream of Bird's platform.&#x20;
* `deleted`
  * When an **outgoing** message was deleted before being sent&#x20;

The message direction will be:

* `outgoing`

This event is available for the following channels:

* `sms`
* `whatsapp`
* `email`
* `line`
* `instagram (including comments and mentions)`
* `facebook`
* `line`
* `viber`
* `linkedin`
* `tiktok`
* `apple business chat`
* `telegram`

**Filter channel ID**

By default, when creating a subscription without specifying any filter, you are going to receive all incoming messages for all channels of that platform e.g WhatsApp

Is it possible to add filters to only receive events for a specific channel ID .

For each subscription, it is only possible to add one channelId filter.

**Filter messageStatus**

For each subscription, it is possible to add none or multiple filters for the `messageStatus`.&#x20;

If the filter is not added, all statuses will be sent. If multiple status filters are added, a webhook will be sent everytime a specified status value is matched.

**Example**

```json
{
  "service": "channels",
  "event": "<channel>.outbound",
  "url": "http://site",
  "signingKey": "key",
  "eventFilters": [
    {
      "key": "channelId",
      "value": "<id>"
    },
    {
      "key": "messageStatus",
      "value": "delivered"
    }
  ]
}

```

| Property        | Description                                                                                                                                                                                               | Example value                                                                              |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| service         | The MEP service which generates the event                                                                                                                                                                 | channels                                                                                   |
| event           | The specific event you are subscribing too. For channels events are scoped to all channels for a specific platform e.g. WhatsApp                                                                          | whatsapp.outbound                                                                          |
| url             | The webhook endpoint                                                                                                                                                                                      | <https://site>                                                                             |
| signingKey      | A value that will be used to validate a webhook                                                                                                                                                           | key                                                                                        |
| eventFilters\[] | Event filters are inclusive, which means you will only get events for filters you add. If you do not add a filter you will get all events (except where you have other webhooks with an explicit filter). | { "key": "channelId", "value": "\<id>" }, { "key": "messageStatus", "value": "delivered" } |

**Example payload**

Here you can find an example of a WhatsApp event&#x20;

```json
{
  "service": "channels",
  "event": "whatsapp.outbound",
  "payload": {
    "id": "4a53460b-1728-4573-bd78-ea1c041a46e6",
    "channelId": "52ad151f-f7b4-46f9-a5c6-d3318e650c84",
    "sender": {
      "connector": {
        "id": "60bf59d6-fc6f-4511-89c4-75d9127d7a7c",
        "identifierValue": "104541186076935"
      }
    },
    "receiver": {
      "contacts": [
        {
          "id": "d4e8935a-5f48-45a5-95a5-ee7ba1e2c5b4",
          "identifierKey": "phonenumber",
          "identifierValue": "+3111111111",
          "type": "to"
        }
      ]
    },
    "reference": "LIbtsEzhedLUt0NwATtN3E",
    "status": "delivered",
    "reason": "",
    "lastStatusAt": "2023-12-13T12:22:33.781Z",
    "createdAt": "2023-12-13T12:22:33.781Z",
    "updatedAt": "2023-12-13T12:22:33.781Z"
  }
}
```

{% hint style="info" %}
sms.outbound events also include the message body in each webhook except when sent as a part of a campaign, when the template ID will be used instead. For other channels, the body is not included&#x20;
{% endhint %}

Here an SMS example

```
json
{
  "id": "9bbd4d52-0000-0000-0000-a663818dffc9",
  "channelId": "a65a0983-0000-0000-0000-1d4c0304ebf6",
  "sender": {
    "connector": {
      "id": "2f117d64-0000-0000-0000-a318203f6c7f",
      "identifierValue": "+46790000000"
    }
  },
  "receiver": {
    "contacts": [
      {
        "id": "8f0feae0-0000-0000-0000-43f102b384c5",
        "identifierKey": "phonenumber",
        "identifierValue": "+590690000000",
        "countryCode": "GP"
      }
    ]
  },
  "body": {
    "type": "text",
    "text": {
      "text": "Hello World !"
    }
  },
  "meta": {},
  "reference": "",
  "parts": [
    {
      "platformReference": "9bbd4d52-0000-0000-0000-a663818dffc9:19c8b0b2-4453-4bd4-a899-062cbbde1a45"
    }
  ],
  "status": "delivered",
  "reason": "",
  "direction": "outgoing",
  "chargeableUnits": 1,
  "lastStatusAt": "2024-04-15T03:37:19.92Z",
  "createdAt": "2024-04-15T03:37:16.441Z",
  "updatedAt": "2024-04-15T03:37:19.92Z"
}

```

## Message Interactions

In addition to message lifecycle events, specific customer actions on a message also lead to message interactions.&#x20;

{% hint style="info" %}
Use the `channels interaction` subscription to keep track of suppression and unsubscription, such as `keyword_unsubscribe` , `keyword_consent` and `link_unsubscribe.` You will need a subscription for each platform (email, sms , WhatsApp etc )
{% endhint %}

To check if a message has any interactions, you can query a specific message using the following endpoint

## List message interactions

> List message interactions

```json
{"openapi":"3.0.3","info":{"title":"Channels","version":"v1"},"servers":[{"url":"https://api.bird.com","description":"Production API"}],"security":[{"accessKey":[]}],"components":{"securitySchemes":{"accessKey":{"description":"Uses the Authorization header: 'AccessKey ' followed by your access key token (e.g., 'Authorization: AccessKey AbCdEfGhIjKlMnOpQrStUvWxYzAbCdEfGhIj')","scheme":"AccessKey","type":"http"}},"schemas":{"MessageInteractionsList":{"type":"object","title":"ChannelMessageInteractionsList","description":"A list of channel messages interactions","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/MessageInteraction"}}}},"MessageInteraction":{"type":"object","title":"ChannelMessageInteraction","additionalProperties":false,"properties":{"id":{"type":"string","format":"uuid"},"messageId":{"type":"string","format":"uuid"},"channelId":{"type":"string","format":"uuid"},"platformId":{"type":"string"},"messagePartId":{"nullable":true,"type":"string","format":"uuid","deprecated":true},"type":{"type":"string","enum":["clicked","delete-request","invalid","opened","read","reported-as-spam","unsubscribe-request","reaction","conversion","subscribe-request","subscribe-consent"]},"messageReference":{"type":"string"},"messagePartsCount":{"type":"number"},"messageTags":{"nullable":true,"type":"array","items":{"type":"string"}},"receiver":{"oneOf":[{"$ref":"#/components/schemas/Connector"},{"$ref":"#/components/schemas/Contacts"}]},"createdAt":{"type":"string","format":"date-time"},"details":{"type":"string"},"context":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}},"metadata":{"description":"Metadata is a free-form object that can be used to store any additional information such as platform specific information\n","type":"object","additionalProperties":true,"properties":{"link":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"}}},"button":{"properties":{"payload":{"type":"string"}}},"reaction":{"type":"object","properties":{"emoji":{"type":"string"},"action":{"type":"string"}}},"conversion":{"type":"object","additionalProperties":false,"properties":{"type":{"type":"string"},"status":{"type":"string"},"method":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"prefetched":{"type":"boolean"}}}}},"Connector":{"type":"object","title":"ChannelConnector","additionalProperties":false,"properties":{"connector":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"identifierValue":{"type":"string"},"annotations":{"$ref":"#/components/schemas/MessageAnnotations"},"types":{"type":"array","items":{"type":"string"}}},"required":["id"]}},"required":["connector"]},"MessageAnnotations":{"type":"object","title":"MessageAnnotations","description":"Annotations are used to add additional information to a message.\nFor email messages, it can be used to specify a custom sender name.\n","additionalProperties":false,"properties":{"name":{"type":"string"}}},"Contacts":{"type":"object","title":"ChannelContacts","additionalProperties":false,"properties":{"contacts":{"type":"array","minLength":1,"items":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string"},"identifierKey":{"type":"string"},"identifierValue":{"type":"string"},"type":{"type":"string"},"countryCode":{"type":"string"},"identifiers":{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"identifierKey":{"type":"string"},"identifierValue":{"type":"string"}}}},"platformAddress":{"type":"string"},"platformAddressSelector":{"type":"string","nullable":true},"annotations":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string"}}}}}}},"required":["contacts"]},"RequestError":{"type":"object","properties":{"code":{"type":"string","description":"A unique code that identifies the error. This code can be used to programmatically identify the error.\n"},"message":{"type":"string","description":"A human-readable message that describes the error. An example is 'The requested resource does not exist: channel not found'.\n"}},"required":["code","message"]}},"responses":{"requestError":{"description":"The request did not pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestError"}}}}}},"paths":{"/workspaces/{workspaceId}/channels/{channelId}/messages/{messageId}/interactions":{"get":{"summary":"List message interactions","operationId":"listChannelMessageInteractions","description":"List message interactions","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageInteractionsList"}}}},"404":{"$ref":"#/components/responses/requestError"}}}}}}
```

### \<channel>.interaction

The following interactions are currently possible

<table><thead><tr><th width="367.3333333333333">Interaction type</th><th>Description</th><th>Supported platforms</th></tr></thead><tbody><tr><td>read</td><td>A user has read a message</td><td>WhatsApp, Facebook, Instagram, Line, RCS</td></tr><tr><td>opened</td><td>A user has opened a message</td><td>Email</td></tr><tr><td>clicked</td><td>A user has clicked a link or clicked a quick reply button</td><td>Email, WhatsApp, Facebook, Instagram, Line, SMS, RCS</td></tr><tr><td>reported-as-spam</td><td>A user has reported a message as spam</td><td>Email</td></tr><tr><td>unsubscribe-request</td><td>A user has requested to unsubscribe from messages</td><td>Email, SMS, RCS</td></tr><tr><td>subscribe-consent</td><td>A user has requested to subscribe to messages</td><td>Email, SMS, RCS</td></tr><tr><td>delete-request</td><td>A user has deleted the recieved message</td><td>-</td></tr><tr><td>reaction</td><td>A user has reacted (or unreacted) to a message with an emoji</td><td>WhatsApp, Facebook, Instagram</td></tr><tr><td>invalid</td><td>Invalid status</td><td>-</td></tr></tbody></table>

By default, when creating a subscription without specifying any filter, you are going to receive all interaction events for all channels of that platform.

Is possible to add filters to only receive interaction for a specific channel ID and specific interaction type.

**Filter channel ID**

By default, when creating a subscription without specifying any filter, you are going to receive all incoming messages for all channels of that platform e.g WhatsApp

Is it possible to add filters to only receive events for a specific channel ID .

For each subscription, it is only possible to add one channelId filter.

**Filter by interactionType**

For each subscription, it is possible to add none or multiple filters for the `interactionType`.&#x20;

If the filter is not added, all interactions will be sent. If multiple interaction filters are added, a webhook will be sent everytime a specified interaction type is matched

**Example**

```json
{
  "service": "channels",
  "event": "<channel>.interaction",
  "url": "http://site",
  "signingKey": "key",
  "eventFilters": [
    {
      "key": "channelId",
      "value": "<id>"
    },
    {
      "key": "interactionType",
      "value": "reaction"
    },
    {
      "key": "interactionType",
      "value": "read"
    }
  ]
}
```

| Property        | Description                                                                                                                                                                                               | Example value                                                                                                                             |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| service         | The MEP service which generates the event                                                                                                                                                                 | channels                                                                                                                                  |
| event           | The specific event you are subscribing too. For channels events are scoped to all channels for a specific platform e.g. WhatsApp                                                                          | whatsapp.outbound                                                                                                                         |
| url             | The webhook endpoint                                                                                                                                                                                      | <https://site>                                                                                                                            |
| signingKey      | A value that will be used to validate a webhook                                                                                                                                                           | key                                                                                                                                       |
| eventFilters\[] | Event filters are inclusive, which means you will only get events for filters you add. If you do not add a filter you will get all events (except where you have other webhooks with an explicit filter). | { "key": "channelId", "value": "{id>" }, { "key": "interactionType", "value": "reaction" }, { "key": "interactionType", "value": "read" } |

**Example payload**

```json
{
  "service": "channels",
  "event": "whatsapp.interaction",
  "payload": {
    "id": "f28b8a73-99d2-11ee-9479-0a58a9feac02",
    "type": "read",
    "createdAt": "2023-12-13T16:16:16Z",
    "messageId": "9c9175e7-63d7-45ae-b945-d21dfea942be",
    "channelId": "199f0353-fcb8-41b2-afd6-614c6baf3850",
    "platformId": "",
    "messageReference": "wamid.HBgLMzE2NTA1MzQyNTkVAgARGBJCMTdBQjdDRjAzNkE3QUIzMEMA",
    "messagePartsCount": 1,
    "receiver": {
      "contacts": [
        {
          "id": "00000000-0000-0000-0000-000000000000",
          "identifierKey": "phonenumber",
          "identifierValue": "311111111"
        }
      ]
    }
  }
}

```

<br>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.bird.com/api/channels-api/message-status-and-interactions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
