Message payloads and uniqueness

Using ADM, you can send messages with a payload of up to 6KB in size. For example, an instant-message program might send a message with a payload like the following:

"data":{"from":"Sam","message":"Hey, Max. How are you?","time":"10/26/2012 09:10:00"}

Alternately, some messages might not include any payload data at all. An example of this is a simple "sync"-type status message, where the message is only intended to notify the app or the user of a sync condition, not to transfer a payload of unique data. In this case, you can pass in an empty data parameter and include a value for the consolidationKey parameter. The consolidation key allows ADM to identify sync messages with redundant payloads and attempt to deliver only one of the messages to your app.

"data":{},"consolidationKey":"Sync"

Sometimes, even payload-carrying messages are not intended to be unique. In this case, too, you would use the consolidationKey parameter to allow ADM to consolidate messages, when possible.

"data":{"message":"Download \"Catcher in the Rye\"","url":"https:\/\/s3.amazon.com\/CatcherInTheRye"},"consolidationKey":"NewDownload"

Request format

To send a message, your server component (from now on referred to as "your server") issues an HTTP POST request that looks similar to this:

The first two lines of the request together comprise the URL for the POST request. The URL must include the registration ID for the specific instance of your app that should receive the message. You should have previously obtained this registration ID from the app instance. For an example of how your app might send your server its registration ID, see the ADM sample app included in the ADM SDK download package.

For the content of the message body, you provide a JSONObject constructed of a string containing the following parameters.

Parameter

Description

Example

data

The payload data to send with the message. The data must be in the form of JSON-formatted key/value pairs; both keys and values must be String values.
The total size of the data cannot be greater than 6KB, including both key(s) and value(s), as well as the quotes that surround them, the ":" character that separates them, the commas that separate the pairs, and the opening and closing brace around the field. Whitespaces between key/value pairs are not included in the calculation of payload size.
If the message does not include payload data, as in the case of a sync message, you can pass in an empty object, for example, "data":{}

Optional value. This is an arbitrary string used to indicate that multiple messages are logically the same and that ADM is allowed to drop previously enqueued messages in favor of this new one. Note that there are no guarantees that the previously enqueued messages will not be delivered. Your consolidation key can be no greater than 64 characters in length.

"consolidationKey":"SyncNow"

expiresAfter

Optional value. The number of seconds that ADM should retain the message if the device is offline. After this time, the message might be discarded. Allowed values range from 60 (1 minute) to 2678400 (31 days), inclusive. The default value is 604800 (1 week).

"expiresAfter":86400

md5

Optional value. This is a base-64-encoded MD5 checksum of the data parameter. If you provide a value for the md5 parameter, ADM verifies its accuracy. If you do not provide a value, the server calculates the value on your behalf. In either case, the server passes the md5 parameter's value through to the device and returns the calculated value within the x-amzn-data-md5 response header.

See an example of calculating an MD5 checksum in the ADM Sample Code folder.

Note: This use of the MD5 hash function is provided not to ensure secure transmission, but only as a convenient check of data integrity.

To ensure your md5 value matches that from ADM servers, you should calculate a value for this parameter as follows:

Sort the message's key-value pairs using a UTF-8 code unit-based comparison of the keys.

Concatenate the series of pairs in the format: "key1:value1,key2:value2".

Remove any white spaces between the key/value pairs. There should be no whitespace between the ':' character and either the keys or values. There should also be no whitespace in between each pair and the ',' character.

Compute the SHA-256 value using the UTF-8 bytes of the resulting string, according to the algorithm defined in RFC 1321.

ADM returns a 200 status code if the message was accepted and is enqueued for delivery to the device. In the case of a 200 code, the response message contains the following parameter in a JSONObject:

registrationID: The current registration ID of the app instance. If this value is different from the one passed in by your server, your server must update its records to use this value.

ADM returns an error (non-200) status code if the message was not successfully accepted. In the case of a non-200 code, the response message might contain the following parameter in the body of the JSONObject:

reason: The reason the request was not accepted.

The following table describes possible ADM error status codes.

Code

Description

Example

400

Some argument of the input was invalid. Possible status codes include: InvalidRegistrationId, InvalidData, InvalidConsolidationKey, InvalidExpiration, InvalidChecksum, InvalidType, and Unregistered.
Unregistered indicates that the app instance associated with the registration ID is no longer available to receive messages. You can receive an Unregistered status code if the owner of the device on which the app instance is registered has changed, or if the app instance has requested to no longer receive messages.
InvalidRegistrationId indicates that the registration ID does not correspond to the sender identified by the provided access token.

"reason":
"InvalidRegistrationId"

401

The access token provided was invalid. The sender should refresh their access token. For information about refreshing an access token, see Request an access token.

The requester has exceeded their maximum allowable rate of messages. The sender might retry later honoring the Retry-After header included in the response. To ensure high availability, ADM limits the number of messages that can be sent over a given period of time. If you have specific capacity requirements, please contact us and provide the following information with your request: your name, company name, your email address, requested TPS (transactions per second) limit, reason.

"reason":"MaxRateExceeded"

500

There was an internal server error. The requester must retry later honoring the Retry-After header included in the response.

n/a

503

The server is temporarily unavailable. The requester must retry later honoring the Retry-After header included in the response.

n/a

The response header contains the following fields:

Field

Description

Example

X-Amzn-Data-md5

The calculated base-64-encoded MD5 checksum of the data field.

X-Amzn-Data-md5: t5psxALRTM7WN30Q8f20tw==

X-Amzn-RequestId

A value created by ADM that uniquely identifies the request. In the unlikely event that you have problems with ADM, Amazon can use this value to troubleshoot the problem.

X-Amzn-RequestId: b55857e7-242d-11e2-8484-47f4656fc00d

Retry-After

This field is returned in the case of a 429, 500, or 503 error response. Retry-After specifies how long the service is expected to be unavailable. This value can be either an integer number of seconds (in decimal) after the time of the response or an HTTP-format date. See the HTTP/1.1 specification, section 14.37, for possible formats for this value.

Retry-After: 120

Content-Type

The content type of the resource: application/json

Content-Type: application/json

X-Amzn-Type-Version

Describes the format of the response.

X-Amzn-Type-Version: com.amazon.device.messaging.ADMSendResult@1.0

Making the message request and handling the response

The following code sample is an example of how your server software might make a request to send a message and then handle the ADM servers' response:

/**
* Request that ADM deliver your message to a specific instance of your app.
*/publicvoidsendMessageToDevice(StringregistrationId,StringaccessToken)throwsException{// JSON payload representation of the message.JSONObjectpayload=newJSONObject();// Define the key/value pairs for your message content and add them to the// message payload.JSONObjectdata=newJSONObject();data.put("firstKey","firstValue");data.put("secondKey","secondValue");payload.put("data",data);// Add a consolidation key. If multiple messages are pending delivery for a particular// app instance with the same consolidation key, ADM will attempt to delivery the most// recently added item.payload.put("consolidationKey","ADM_Enqueue_Sample");// Add an expires-after value to the message of 1 day. If the targeted app instance does not// come online within the expires-after window, the message will not be delivered.payload.put("expiresAfter",86400);// Convert the message from a JSON object to a string.StringpayloadString=payload.toString();// Establish the base URL, including the section to be replaced by the registration// ID for the desired app instance. Because we are using String.format to create// the URL, the %1$s characters specify the section to be replaced.StringadmUrlTemplate="https://api.amazon.com/messaging/registrations/%1$s/messages";URLadmUrl=newURL(String.format(admUrlTemplate,registrationId));// Generate the HTTPS connection for the POST request. You cannot make a connection// over HTTP.HttpsURLConnectionconn=(HttpsURLConnection)admUrl.openConnection();conn.setRequestMethod("POST");conn.setDoOutput(true);// Set the content type and accept headers.conn.setRequestProperty("content-type","application/json");conn.setRequestProperty("accept","application/json");conn.setRequestProperty("X-Amzn-Type-Version","com.amazon.device.messaging.ADMMessage@1.0");conn.setRequestProperty("X-Amzn-Accept-Type","com.amazon.device.messaging.ADMSendResult@1.0");// Add the authorization token as a header.conn.setRequestProperty("Authorization","Bearer "+accessToken);// Obtain the output stream for the connection and write the message payload to it.OutputStreamos=conn.getOutputStream();os.write(payloadString.getBytes(),0,payloadString.getBytes().length);os.flush();conn.connect();// Obtain the response code from the connection.intresponseCode=conn.getResponseCode();// Check if we received a failure response, and if so, get the reason for the failure.if(responseCode!=200){if(responseCode==401){// If a 401 response code was received, the access token has expired. The token should be refreshed// and this request can be retried.}StringerrorContent=parseResponse(conn.getErrorStream());thrownewRuntimeException(String.format("ERROR: The enqueue request failed with a "+"%d response code, with the following message: %s",responseCode,errorContent));}else{// The request was successful. The response contains the canonical Registration ID for the specific instance of your// app, which might be different that the one used for the request.StringresponseContent=parseResponse(conn.getInputStream());JSONObjectparsedObject=newJSONObject(responseContent);StringcanonicalRegistrationId=parsedObject.getString("registrationID");// Check if the two Registration IDs are different.if(!canonicalRegistrationId.equals(registrationId)){// At this point the data structure that stores the Registration ID values should be updated// with the correct Registration ID for this particular app instance.}}}privateStringparseResponse(InputStreamin)throwsException{// Read from the input stream and convert into a String.InputStreamReaderinputStream=newInputStreamReader(in);BufferedReaderbuff=newBufferedReader(inputStream);StringBuildersb=newStringBuilder();Stringline=buff.readLine();while(line!=null){sb.append(line);line=buff.readLine();}returnsb.toString();}