Sending and Receiving Messages

When a user sends a message to a business, the user's device sends the message to the Messages for Business server. The Messages for Business server forwards the message to the Messaging Service Provider (MSP) platform by making a POST request to the /message endpoint. For more information on receiving messages, see Messages Received.

When a business replies to a user’s text message, the MSPs platform forwards the reply to the Messages for Business server. To forward the reply, the MSP platform prepares the message for delivery and sends a POST request to the Messages for Business /message endpoint. For more information on sending messages, see Messages Sent.

There is no time limit for a business to reply or send a new message to a user, but consider the following guidelines and features available:

In this section, you’ll learn how to listen for, receive, and validate incoming messages.

TIP You can initiate a conversation in the Messages app by using the business ID directly as a recipient in Compose Message. The format is urn:biz:<your-business-id>.

Exercise: Listening for Incoming Messages

In this exercise, you run a simple web service and listen to message requests. Send a text message from the Messages app on a device to your business and observe the request of the text message.

Perform the following tasks on your test server:

  1. Locate the zip folder and unzip it.
  2. Locate and run the 00_listening.py file.
  3. Listen to POST requests at: https://<yourCSPEndpoint>/message.

Sending a Message from Your iOS Device

Send a text message from the Messages app on an iOS device to your business.

  1. From an iOS device, open Messages.
  2. Put in your business URN identifier (urn:biz:<your registered business id>) as the recipient.
  3. Compose a simple text message, such as "Hello business!", and tap send.

Expected MSP server response

By default, on your MSP test server you'll receive at least two messages: typing_start and the actual message. The typing indicator in the output automatically displays on the console and is not part of the typing_start indicator.

Just received a message!
Just received a message!

TIP When using a HA Proxy for your messaging endpoint, make sure to add Apple’s Class A address block to accept incoming traffic: 17.0.0/8.

Exercise: Receiving a Text Message

In this exercise, you receive, decode, and read an incoming text message.

TIP Once a message has been delivered, you cannot retrieve it.

Complete the following tasks:

  1. Send a text message from the Messages app to your business.
  2. Receive the message payload.
  3. Uncompress, using GZIP, and print the payload.

Perform the following tasks on your test server:

  1. Locate and run the 01_receiving_text_message.py file.
  2. When finished, save the source_id for use later in the tutorial.

Expected MSP server response

You should see two types of information: the opaque user ID and the capability-list. The opaque user ID is the source ID when receiving messages. You can retrieve the opaque user ID by listening to incoming messages. The capability list tells you the user's device capability to support certain types of messages type. For more information on sending messages, see HTTP Headers

User is typing...
Just received a text message!
Message body: Nice to meet you
Source ID: <opaque user ID>;
Capability List: <capability-list?;

Messages for Business uses the opaque user ID to uniquely identify conversations that occur between a Business ID and a user. The user's multiple devices share the same opaque user ID and it persists over long periods of time (years). It isn't shared between businesses, even if they're with the same MSP. When the user initiates a conversation with another Business ID, they'll receive a new opaque user ID for that conversation.

Exercise: Validating a Received Message

In this exercise, you verify the authorization header of a received message. For more information on validating messages, see Validating a Message.

Complete the following tasks:

  1. Extract the JWT (JSON Web Token) string from headers.
  2. Verify the signature and audience by decoding the JWT string.

When receiving a message on your messaging platform, the JWT audience (aud) value in the JWT payload, is your MSP ID. Do not use your Business ID to validate.

TIP The HTTP header field names are case-insensitive, according to https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html.

Perform the following tasks on your test server:

  1. Locate and run the config.py file to completion.
  2. Locate and run the 02_verify_message.py file to completion.

Expected MSP server response

By default, you'll receive at least two messages: one is for typing start, and the other is for the actual message. The output automatically displays the typing indicator on the console and is not the typing_start indicator. Observe the request of the text message.

Authorization succeeded.
Authorization succeeded.

If you don't receive any requests from Apple Messages for Business server, check the following:

  • Make sure your endpoint SSL certificate fulfills the requirements. See Configuring Your MSP Test Server.
  • Make sure your server is listening to POST requests on https://<your.domain>/<path>/message.
  • Make sure you are using the iMessage service for outgoing messages. You can check the iMessage setting by going to Settings > Messages.
  • Make sure your messages actually were sent from the device. If you are seeing a progress bar that runs for a long time, check your internet connection and iMessage settings. For more information, see If you get an error when trying to activate iMessage or FaceTime.
  • Make sure there is no software blocking requests or closing the connection. Be aware that your service may be blocking incoming requests due to strict rules. For example, the port number is in the "hostname" HTTP header even though it's optional.

Receiving Closed Conversation Messages

Users can opt out of receiving any further messages from a brand. This is also known as closing a conversation.

A user closes a conversation by going to the tray view in Messages and left swipe a conversation. This action reveals two buttons: a mute icon and a delete icon. The user clicks the delete icon and agrees to delete the conversation. This action sends a “close” message for a Messages for Business conversation. Apple refers to this action as “delete” on the user's device, and the MSP and brand receive a “close” conversation from the standpoint of the brand and MSP.

When a user closes a conversation, Messages for Business sends a message of type close to your MSP platform. Your brand cannot send any further messages to this user. If they try, Messages for Business service sends an HTTP 410 | Gone response. We require MSPs to keep a table of closed conversations and prevent brands from sending messages to users who have opted out.

The user can re-initiate the conversation. They can resume the conversation from any valid entry-point for the brand. When they do, they will return with the same opaque user ID as before. For more information, see Receiving a Closed Conversation.

If the user chooses to stop receiving messages, Messages for Business considers the conversation closed and sends a message of type close to the MSP platform.

{
  "id": "UUID-IDENTIFIER-FOR-MESSAGE",
  "sourceId": "OPAQUE-USER-ID",
  "destinationId": "BUSINESS-ID",
  "type": "close",
  "v": 1
}

Typing Indicators

Your platform can receive these message types from user devices. Your platform should parse these messages and send the typing indication to your agents. For more information, see TypingIndicatorMessage.

Tapback Reactions

Your platform can receive these message types from user devices. Your platform should parse these messages and send the typing indication to your agents. For more information, see Tapbak Reactions.

Exercise: Sending a Text Message

In this exercise, you send a text message payload.

Complete the following tasks:

  1. Successfully derive the binary MSP secret from secret passphrase.
  2. Create a valid JWT header using your MSP secret. See Authorizing Messages.
  3. Get the destination_id from the response in Exercise: Receiving a Text Message to respond.
  4. Copy and paste the destination_id into the Listing 03_send_text_message.py code.
  5. Assemble a message header and payload.
  6. Send a message.

When sending a message from your messaging platform, the JWT issuer value (iss in the JWT payload) should be the MSP ID. Using Business ID in this instance results in authorization failures.

TIP The timestamp unit, iat in the JWT payload, should be in seconds, not milliseconds.

The following JWT header creation code makes the authorization header field token.

Perform the following tasks on your test server:

  1. Locate and run the jwt_util.py file to completion.
  2. Locate and edit the 03_send_text_message.py file by inserting the destination_id, located towards the bottom of the file, with the source_id from the output in Exercise: Receiving a Text Message.
  3. Save and run the 03_send_text_message.py file.

Expected MSP server response

You should see the following message on your MSP server.

Messages for Business server return code: 200

TIP You can find sample of HTTP Headers used generally for messaging on Messages for Business in the sample code under samples/message_headers.json.

If you are unable to send a text message, then check the following:

  • Go through the Messages for Business documentation and the tutorial.
  • Examine the response body for potential errors.
  • If you get an error code, refer to the table below.
Error Troubleshooting Tips
401 "unauthorized" error Verify the following: Use your MSP ID to encode the JWT token, and your Business ID in headers and payload. Base64-decode the textual secret when generating your JWT token. Your JWT timestamp (iat value) is in seconds and not in milliseconds or another unit. The "Bearer" prefix is in the authorization header.
404 "Not Found: No device registrations for user" error These errors occur when a brand messages a user during their iPhone or device upgrade cycle. The carrier has an inactivated status on the SIM card for a particular device. Our messaging systems have the user account marked as messageable and active but the error comes back from the carrier network.

Once the upgrade cycle is complete, users will again receive messages without any further work for the MSP or the user. They will not, however, receive messages that were returned as 404. We recommend those be sent again or marked in your system as undelivered.
410 "session expired" error The customer you are attempting to message deleted the conversation from the messages tray on their device. This brand will be unable to message the user.

If the user resumes the conversation, by clicking on a link or entry-point, the conversation will continue. The user will have the same opaque ID and the brand will again be able to message the user.

Use the following matrix to troubleshoot problems you may encounter with the allow list.

Apple ID Allow Listed Closed Conv. Device to MSP MSP to Device
Ok Ok
Not delivered Not delivered
Removed Ok
Logout* Ok 404 Resource Not Found
n/a No email sent
Ok, reopens conversation. 410 Resource Gone

* Occurs only if the user is logged out on all devices for a particular Apple ID. Otherwise, messages are delivered to devices still logged in with the Apple ID.

Exercise: Sending an Image Attachment

A message can include one or more attachments. An attachment can be an image, PDF, or other file type, and it must be smaller than 100 MB. For more information about attachments, see Sending Messages with Attachments.

In this exercise, you'll send a message with an image attachment.

  1. Perform a pre-upload request to retrieve an attachment upload URL.
  2. Encrypt the attachment data.
  3. Upload the encrypted attachment data to the retrieved attachment upload URL.
  4. Send the message with metadata of the attachment.

Perform the following task on your test server:

  • Locate and run the attachment_cipher.py file.

Send the Message with Base64-encoded Images

In this exercise, you send a message with Base64-encoded image attachments. In the message payload body, you need an Unicode Object Replacement Character (\uFFFC) as a placeholder for each attachment you send.

TIP Base64 is a standard for converting binary data to a viewable text string. For more information, see How to convert an image to base64 encoding. There are also online drag-and-drop encoders. To encode or decode Base64, see Base64 Decode and Encode

Perform the following tasks on your test server:

  1. Locate and edit the 04_send_image_attachment.py file by inserting the destination_id, located towards the bottom of the file, with the source_id from the output in Exercise: Receiving a Text Message.
  2. Save and run the 04_send_image_attachment.py file.

Expected MSP server response

You should see the following message appear on your MSP server.

Messages for Business server return code: 200

Expected client device response

If the encrypted data does not display correctly in the Message client, check your encryption implementation.

If attachments are not showing up in the Messages app, check the following:

  • Make sure in the message body you have a Unicode Object Replacement Character (\uFFFc) for each attachment you are sending.

  • Make sure you are using Base64-encoding for the attachment signature.

  • If the sent images are displayed as icons in the Messages app, then verify the following:

    • Encrypt the attachment using the correct algorithm.
    • Do not compress the data when sending.
    • Uploaded data is not corrupted.
    • Do not subsequently modify the data using network nodes.

To verify that your encryption implementation is correct:

  • In your encryption method, use the following encryption key rather than a randomly generated one: 12E9F08B6B0CCC36DF688BF167FAF7BF3E7E696D1A66E98C9E9949131C9F8E07 (hex-encoded).

    1. Hex-decode the string.
    2. Encrypt the string "12345" using the above listed encryption key.
    3. The Base16-encoded result should be 7634125F65.

To verify that your decryption implementation is correct:

  • Encrypt then decrypt any given string. The result should be the same as the input.

Exercise: Downloading Attachments

In this exercise, you'll receive a message containing attachments. You need to download and decrypt the attachments. For more information, see Downloading and Decrypting an Attachment.

TIP In general, the URL field in the attachments array should not be URI-encoded. You can include a mix of unescaped and escaped characters if that is how the client device uses the link.

Complete the following tasks:

  1. Receive a message payload with attachments.
  2. For each attachment perform a /preDownload request to get the download URL.
  3. Convert the provided hexadecimal signature from the payload to a Base64-encoded signature.
  4. Download the attachment using the download URL.
  5. Decrypt attachment data and save to file.

Save the message attachments as local files under the current path.

NOTE You always get a download URL from the /preDownload step even if the parameters are not correctly set. However, the downloaded data is not valid in such a case.

Perform the following task on your test server:

  • Locate and run the 05_downloading_attachments.py file.

Expected MSP server response

2 attachments found in the message.
writing to local file: 52106351067__A31A08AE-A449-4EDD-A735-458D17ADF9EA.JPG
writing to local file: 52106351346__8CEE7676-0E8C-4D19-83B5-C680836837CC.JPG

Error codes

Error Troubleshooting Tip
400 "not authorized" Make sure you are using the correct encoding for attachment signature. It should be a Base64-encoded string.
400 error when calling /decodePayload for interactive data payload Make sure you have "bid" as a header in your request.
Problems decrypting the attachment Make sure you are using the correct algorithm for decryption. Once you are confident that the encryption algorithm works, try to encrypt and immediately decrypt the file. If the file can be recovered, then the decryption worked.

Ensure Correct Signature Fields

Below are the uses of Base16- and Base64-encoded signature fields used by Messages for Business:

  • When receiving a message from Messages for Business, the signature field of the attachment is Base16-encoded, or hexadecimal.
  • When sending the signature header in the preDownload request, the value is Base64-encoded.
  • When sending a message from your MSP to Messages for Business, Messages for Business calls the signature field signature-Base64 because it is Base64-encoded.