diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/MessageAsyncApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/MessageAsyncApi.java
deleted file mode 100644
index c52701cfb4..0000000000
--- a/labs/sqs/src/main/java/org/jclouds/sqs/MessageAsyncApi.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.sqs;
-
-import static org.jclouds.sqs.reference.SQSParameters.ACTION;
-import static org.jclouds.sqs.reference.SQSParameters.VERSION;
-
-import java.util.Set;
-
-import javax.ws.rs.FormParam;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-
-import org.jclouds.Constants;
-import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.rest.annotations.FormParams;
-import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
-import org.jclouds.rest.annotations.VirtualHost;
-import org.jclouds.rest.annotations.XMLResponseParser;
-import org.jclouds.sqs.domain.Message;
-import org.jclouds.sqs.domain.MessageIdAndMD5;
-import org.jclouds.sqs.options.ReceiveMessageOptions;
-import org.jclouds.sqs.options.SendMessageOptions;
-import org.jclouds.sqs.xml.MessageHandler;
-import org.jclouds.sqs.xml.ReceiveMessageResponseHandler;
-import org.jclouds.sqs.xml.RegexMessageIdAndMD5Handler;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * Provides access to SQS via their REST API.
- * <p/>
- * 
- * @author Adrian Cole
- */
-@RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = "{" + Constants.PROPERTY_API_VERSION + "}")
-@VirtualHost
-public interface MessageAsyncApi {
-
-   /**
-    * @see SQSApi#delete
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "DeleteMessage")
-   ListenableFuture<Void> delete(@FormParam("ReceiptHandle") String receiptHandle);
-
-   /**
-    * @see SQSApi#changeVisibility
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "ChangeMessageVisibility")
-   ListenableFuture<Void> changeVisibility(@FormParam("ReceiptHandle") String receiptHandle,
-         @FormParam("VisibilityTimeout") int visibilityTimeout);
-
-   /**
-    * @see SQSApi#send
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "SendMessage")
-   @ResponseParser(RegexMessageIdAndMD5Handler.class)
-   ListenableFuture<? extends MessageIdAndMD5> send(@FormParam("MessageBody") String message);
-
-   /**
-    * @see SQSApi#send
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "SendMessage")
-   @ResponseParser(RegexMessageIdAndMD5Handler.class)
-   ListenableFuture<? extends MessageIdAndMD5> send(@FormParam("MessageBody") String message, SendMessageOptions options);
-
-   /**
-    * @see SQSApi#receive
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "ReceiveMessage")
-   @XMLResponseParser(MessageHandler.class)
-   ListenableFuture<Message> receive();
-
-   /**
-    * @see SQSApi#receive
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "ReceiveMessage")
-   @XMLResponseParser(MessageHandler.class)
-   ListenableFuture<? extends Message> receive(ReceiveMessageOptions options);
-
-   /**
-    * @see SQSApi#receive
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "ReceiveMessage")
-   @XMLResponseParser(ReceiveMessageResponseHandler.class)
-   ListenableFuture<? extends Set<? extends Message>> receive(@FormParam("MaxNumberOfMessages") int max);
-
-   /**
-    * @see SQSApi#receive
-    */
-   @POST
-   @Path("/")
-   @FormParams(keys = ACTION, values = "ReceiveMessage")
-   @XMLResponseParser(ReceiveMessageResponseHandler.class)
-   ListenableFuture<? extends Set<? extends Message>> receive(@FormParam("MaxNumberOfMessages") int max,
-         ReceiveMessageOptions options);
-
-}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/SQSApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/SQSApi.java
index 51636dbf0d..8804e24904 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/SQSApi.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/SQSApi.java
@@ -28,6 +28,9 @@ import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
+import org.jclouds.sqs.features.MessageApi;
+import org.jclouds.sqs.features.PermissionApi;
+import org.jclouds.sqs.features.QueueApi;
 
 import com.google.common.annotations.Beta;
 import com.google.inject.Provides;
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/SQSAsyncApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/SQSAsyncApi.java
index 164b3e98e7..c9a8f0ed56 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/SQSAsyncApi.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/SQSAsyncApi.java
@@ -29,6 +29,9 @@ import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.VirtualHost;
+import org.jclouds.sqs.features.MessageAsyncApi;
+import org.jclouds.sqs.features.PermissionAsyncApi;
+import org.jclouds.sqs.features.QueueAsyncApi;
 
 import com.google.common.annotations.Beta;
 import com.google.inject.Provides;
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams.java b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams.java
new file mode 100644
index 0000000000..1a722aa84c
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.binders;
+
+import java.util.Map;
+
+import org.jclouds.aws.binders.BindTableToIndexedFormParams;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.MapBinder;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.ImmutableTable.Builder;
+import com.google.common.collect.Maps;
+
+/**
+ * @author Adrian Cole
+ */
+public class BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams extends BindTableToIndexedFormParams
+      implements MapBinder {
+
+   protected BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams() {
+      super("ChangeMessageVisibilityBatchRequestEntry.%d.Id",
+            "ChangeMessageVisibilityBatchRequestEntry.%d.ReceiptHandle",
+            "ChangeMessageVisibilityBatchRequestEntry.%d.VisibilityTimeout");
+   }
+
+   public Map<String, String> idReceiptHandle(Iterable<String> input) {
+      return Maps.uniqueIndex((Iterable<String>) input, new Function<String, String>() {
+         int index = 1;
+
+         @Override
+         public String apply(String input) {
+            return index++ + "";
+         }
+      });
+   }
+
+   @SuppressWarnings("unchecked")
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+      Map<String, String> idReceiptHandle = (Map<String, String>) postParams.get("idReceiptHandle");
+      if (idReceiptHandle == null) {
+         idReceiptHandle = idReceiptHandle((Iterable<String>) postParams.get("receiptHandles"));
+      }
+      int visibilityTimeout = (Integer) postParams.get("visibilityTimeout");
+
+      Builder<Object, Object, Object> builder = ImmutableTable.builder();
+      for (Map.Entry<?, ?> entry : idReceiptHandle.entrySet())
+         builder.put(entry.getKey(), entry.getValue(), visibilityTimeout);
+      return bindToRequest(request, (Object) builder.build());
+   }
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindDeleteMessageBatchRequestEntryToIndexedFormParams.java b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindDeleteMessageBatchRequestEntryToIndexedFormParams.java
new file mode 100644
index 0000000000..dd1ba5e9ad
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindDeleteMessageBatchRequestEntryToIndexedFormParams.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.binders;
+
+import org.jclouds.aws.binders.BindMapToIndexedFormParams;
+
+/**
+ * @author Adrian Cole
+ */
+public class BindDeleteMessageBatchRequestEntryToIndexedFormParams extends BindMapToIndexedFormParams {
+
+   protected BindDeleteMessageBatchRequestEntryToIndexedFormParams() {
+      super("DeleteMessageBatchRequestEntry.%d.Id", "DeleteMessageBatchRequestEntry.%d.ReceiptHandle");
+   }
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindSendMessageBatchRequestEntryToIndexedFormParams.java b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindSendMessageBatchRequestEntryToIndexedFormParams.java
new file mode 100644
index 0000000000..748c7d0fcb
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindSendMessageBatchRequestEntryToIndexedFormParams.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.binders;
+
+import org.jclouds.aws.binders.BindMapToIndexedFormParams;
+
+/**
+ * @author Adrian Cole
+ */
+public class BindSendMessageBatchRequestEntryToIndexedFormParams extends BindMapToIndexedFormParams {
+
+   protected BindSendMessageBatchRequestEntryToIndexedFormParams() {
+      super("SendMessageBatchRequestEntry.%d.Id", "SendMessageBatchRequestEntry.%d.MessageBody");
+   }
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams.java b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams.java
new file mode 100644
index 0000000000..0727a47989
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/binders/BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.binders;
+
+import java.util.Map;
+
+import org.jclouds.aws.binders.BindTableToIndexedFormParams;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.MapBinder;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.ImmutableTable.Builder;
+import com.google.common.collect.Maps;
+
+/**
+ * @author Adrian Cole
+ */
+public class BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams extends BindTableToIndexedFormParams
+      implements MapBinder {
+
+   protected BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams() {
+      super("SendMessageBatchRequestEntry.%d.Id", "SendMessageBatchRequestEntry.%d.MessageBody",
+            "SendMessageBatchRequestEntry.%d.DelaySeconds");
+   }
+
+   public Map<String, String> idMessageBody(Iterable<String> input) {
+      return Maps.uniqueIndex((Iterable<String>) input, new Function<String, String>() {
+         int index = 1;
+
+         @Override
+         public String apply(String input) {
+            return index++ + "";
+         }
+      });
+   }
+
+   @SuppressWarnings("unchecked")
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+      Map<String, String> idMessageBody = (Map<String, String>) postParams.get("idMessageBody");
+      if (idMessageBody == null) {
+         idMessageBody = idMessageBody((Iterable<String>) postParams.get("messageBodies"));
+      }
+      int delaySeconds = (Integer) postParams.get("delaySeconds");
+
+      Builder<Object, Object, Object> builder = ImmutableTable.builder();
+      for (Map.Entry<?, ?> entry : idMessageBody.entrySet())
+         builder.put(entry.getKey(), entry.getValue(), delaySeconds);
+      return bindToRequest(request, (Object) builder.build());
+   }
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/config/SQSRestClientModule.java b/labs/sqs/src/main/java/org/jclouds/sqs/config/SQSRestClientModule.java
index 1839c951b6..09b4882d07 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/config/SQSRestClientModule.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/config/SQSRestClientModule.java
@@ -27,14 +27,14 @@ import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
 import org.jclouds.http.annotation.ServerError;
 import org.jclouds.rest.ConfiguresRestClient;
-import org.jclouds.sqs.MessageApi;
-import org.jclouds.sqs.MessageAsyncApi;
-import org.jclouds.sqs.PermissionApi;
-import org.jclouds.sqs.PermissionAsyncApi;
-import org.jclouds.sqs.QueueApi;
-import org.jclouds.sqs.QueueAsyncApi;
 import org.jclouds.sqs.SQSApi;
 import org.jclouds.sqs.SQSAsyncApi;
+import org.jclouds.sqs.features.MessageApi;
+import org.jclouds.sqs.features.MessageAsyncApi;
+import org.jclouds.sqs.features.PermissionApi;
+import org.jclouds.sqs.features.PermissionAsyncApi;
+import org.jclouds.sqs.features.QueueApi;
+import org.jclouds.sqs.features.QueueAsyncApi;
 import org.jclouds.sqs.handlers.ParseSQSErrorFromXmlContent;
 import org.jclouds.sqs.handlers.SQSErrorRetryHandler;
 
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/domain/BatchError.java b/labs/sqs/src/main/java/org/jclouds/sqs/domain/BatchError.java
new file mode 100644
index 0000000000..45696f4d70
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/domain/BatchError.java
@@ -0,0 +1,155 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+
+/**
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QueryDeleteMessageBatch.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class BatchError {
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromErrorEntry(this);
+   }
+
+   public static class Builder {
+
+      private String id;
+      private boolean senderFault;
+      private String code;
+      private String message;
+
+      /**
+       * @see BatchError#getId()
+       */
+      public Builder id(String id) {
+         this.id = id;
+         return this;
+      }
+
+      /**
+       * @see BatchError#isSenderFault()
+       */
+      public Builder senderFault(boolean senderFault) {
+         this.senderFault = senderFault;
+         return this;
+      }
+
+      /**
+       * @see BatchError#getCode()
+       */
+      public Builder code(String code) {
+         this.code = code;
+         return this;
+      }
+
+      /**
+       * @see BatchError#getMessage()
+       */
+      public Builder message(String message) {
+         this.message = message;
+         return this;
+      }
+
+      public BatchError build() {
+         return new BatchError(id, senderFault, code, message);
+      }
+
+      public Builder fromErrorEntry(BatchError in) {
+         return id(in.getId()).senderFault(in.isSenderFault()).code(in.getCode()).message(in.getMessage());
+      }
+   }
+
+   private final String id;
+   private final boolean senderFault;
+   private final String code;
+   private final String message;
+
+   private BatchError(String id, boolean senderFault, String code, String message) {
+      this.id = checkNotNull(id, "id");
+      this.senderFault = checkNotNull(senderFault, "senderFault of %s", id);
+      this.code = checkNotNull(code, "code of %s", id);
+      this.message = checkNotNull(message, "message of %s", id);
+   }
+
+   /**
+    * The Id name that you assigned to the message.
+    */
+   public String getId() {
+      return id;
+   }
+
+   /**
+    * 
+    */
+   public boolean isSenderFault() {
+      return senderFault;
+   }
+
+   /**
+    * A short string description of the error.
+    */
+   public String getCode() {
+      return code;
+   }
+
+   /**
+    * A description of the error.
+    */
+   public String getMessage() {
+      return message;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(id);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null || getClass() != obj.getClass())
+         return false;
+      BatchError that = BatchError.class.cast(obj);
+      return Objects.equal(this.id, that.id);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("id", id).add("senderFault", senderFault)
+            .add("message", message).add("code", code).toString();
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/domain/BatchResult.java b/labs/sqs/src/main/java/org/jclouds/sqs/domain/BatchResult.java
new file mode 100644
index 0000000000..cbb2d4b496
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/domain/BatchResult.java
@@ -0,0 +1,146 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.collect.ForwardingMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+/**
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/MessageLifecycle.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class BatchResult<V> extends ForwardingMap<String, V> {
+
+   public static <V> Builder<V> builder() {
+      return new Builder<V>();
+   }
+
+   public Builder<V> toBuilder() {
+      return BatchResult.<V> builder().fromBatchResult(this);
+   }
+
+   public static class Builder<V> {
+
+      private ImmutableMap.Builder<String, V> results = ImmutableMap.<String, V> builder();
+      private ImmutableSet.Builder<BatchError> errors = ImmutableSet.<BatchError> builder();
+
+      /**
+       * @see BatchResult#getErrors()
+       */
+      public Builder<V> addError(BatchError error) {
+         this.errors.add(checkNotNull(error, "error"));
+         return this;
+      }
+
+      /**
+       * @see BatchResult#getErrors()
+       */
+      public Builder<V> errors(Iterable<BatchError> errors) {
+         this.errors = ImmutableSet.<BatchError> builder().addAll(checkNotNull(errors, "errors"));
+         return this;
+      }
+
+      /**
+       * @see BatchResult#get
+       */
+      public Builder<V> putAll(Map<String, V> results) {
+         this.results.putAll(checkNotNull(results, "results"));
+         return this;
+      }
+
+      /**
+       * @see BatchResult#get
+       */
+      public Builder<V> put(String name, V value) {
+         this.results.put(checkNotNull(name, "name"), checkNotNull(value, "value"));
+         return this;
+      }
+
+      public BatchResult<V> build() {
+         return new BatchResult<V>(results.build(), errors.build());
+      }
+
+      public Builder<V> fromBatchResult(BatchResult<V> in) {
+         return putAll(in).errors(in.getErrors().values());
+      }
+   }
+
+   private final Map<String, V> results;
+   private final Map<String, BatchError> errors;
+
+   private BatchResult(Map<String, V> results, Iterable<BatchError> errors) {
+      this.results = ImmutableMap.copyOf(checkNotNull(results, "results"));
+      this.errors = Maps.uniqueIndex(checkNotNull(errors, "errors"), new Function<BatchError, String>() {
+         @Override
+         public String apply(BatchError in) {
+            return in.getId();
+         }
+
+      });
+   }
+
+   @Override
+   protected Map<String, V> delegate() {
+      return results;
+   }
+
+   /**
+    * Errors indexed by requestor supplied id
+    */
+   public Map<String, BatchError> getErrors() {
+      return errors;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(results, errors);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null || getClass() != obj.getClass())
+         return false;
+      @SuppressWarnings("unchecked")
+      BatchResult<V> that = BatchResult.class.cast(obj);
+      return Objects.equal(this.results, that.results) && Objects.equal(this.errors, that.errors);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("results", results).add("errors", errors).toString();
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/MessageApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/features/MessageApi.java
similarity index 52%
rename from labs/sqs/src/main/java/org/jclouds/sqs/MessageApi.java
rename to labs/sqs/src/main/java/org/jclouds/sqs/features/MessageApi.java
index 63fdc8ce4a..fa4b96ae4a 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/MessageApi.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/features/MessageApi.java
@@ -16,23 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.sqs;
+package org.jclouds.sqs.features;
 
 import java.net.URI;
-import java.util.Set;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.concurrent.Timeout;
+import org.jclouds.sqs.domain.BatchResult;
 import org.jclouds.sqs.domain.Message;
 import org.jclouds.sqs.domain.MessageIdAndMD5;
 import org.jclouds.sqs.options.ReceiveMessageOptions;
 import org.jclouds.sqs.options.SendMessageOptions;
 
+import com.google.common.collect.Table;
+
 /**
  * Provides access to SQS via their REST API.
  * <p/>
  * 
- * @see SQSAsyncApi
+ * @see MessageAsyncApi
  * @author Adrian Cole
  */
 @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
@@ -72,6 +76,40 @@ public interface MessageApi {
     */
    void delete(String receiptHandle);
 
+   /**
+    * Currently, you can send up to 10 {@link #delete} requests.
+    * 
+    * <h4>Example usage</h4>
+    * 
+    * <pre>
+    * BatchResult<String> results = api.delete(ImmutableMap.<String, String>builder()
+    *                                  .put("id1", "handle1")
+    *                                  .put("id2", "handle2")
+    *                                  .build());
+    * 
+    * if (results.keySet().equals(ImmutableSet.of("id", "id2"))
+    *    // all ok
+    * else
+    *   results.getErrors();
+    * </pre>
+    * 
+    * @param idReceiptHandle
+    *           id for correlating the result to receipt handle
+    * @return result that contains success or errors of the operation
+    * @see #delete(String)
+    */
+   BatchResult<String> delete(Map<String, String> idReceiptHandle);
+
+   /**
+    * Same as {@link #delete(Map)}, except that we generate numeric ids starting
+    * with {@code 1}
+    * 
+    * @param receiptHandles
+    *           receipt handles to delete
+    * @see #delete(Map)
+    */
+   BatchResult<String> delete(Iterable<String> receiptHandles);
+
    /**
     * The ChangeMessageVisibility action changes the visibility timeout of a
     * specified message in a queue to a new value. The maximum allowed timeout
@@ -114,6 +152,77 @@ public interface MessageApi {
     */
    void changeVisibility(String receiptHandle, int visibilityTimeout);
 
+   /**
+    * Currently, you can send up to 10 {@link #changeVisibility} requests.
+    * 
+    * action. <h4>Example usage</h4>
+    * 
+    * <pre>
+    * BatchResult<String> results = api.changeVisibility(ImmutableTable.<String, String, Integer>builder()
+    *                                  .put("id1", "handle1", 45)
+    *                                  .put("id2", "handle2", 10)
+    *                                  .build());
+    * 
+    * if (results.keySet().equals(ImmutableSet.of("id", "id2"))
+    *    // all ok
+    * else
+    *   results.getErrors();
+    * </pre>
+    * 
+    * @param idReceiptHandleVisibilityTimeout
+    *           id for correlating the result, receipt handle, and visibility
+    *           timeout
+    * @return result that contains success or errors of the operation
+    * @see #changeVisibility(String, int)
+    */
+   BatchResult<String> changeVisibility(Table<String, String, Integer> idReceiptHandleVisibilityTimeout);
+
+   /**
+    * Same as {@link #changeVisibility(Table)}, except that we generate numeric
+    * ids starting with {@code 1}
+    * 
+    * @param receiptHandleVisibilityTimeout
+    *           receipt handle to visibility timeout
+    * @see #changeVisibility(Table)
+    */
+   BatchResult<? extends MessageIdAndMD5> changeVisibility(Map<String, Integer> receiptHandleVisibilityTimeout);
+
+   /**
+    * Currently, you can send up to 10 {@link #changeVisibility} requests.
+    * 
+    * action. <h4>Example usage</h4>
+    * 
+    * <pre>
+    * BatchResult<String> results = api.changeVisibility(ImmutableMap.<String, String>builder()
+    *                                  .put("id1", "handle1")
+    *                                  .put("id2", "handle2")
+    *                                  .build(), 45);
+    * 
+    * if (results.keySet().equals(ImmutableSet.of("id", "id2"))
+    *    // all ok
+    * else
+    *   results.getErrors();
+    * </pre>
+    * 
+    * @param idReceiptHandle
+    *           id for correlating the result to receipt handle
+    * @param visibilityTimeout
+    *           The new value for the message's visibility timeout (in seconds).
+    * @return result that contains success or errors of the operation
+    * @see #changeVisibility(String, int)
+    */
+   BatchResult<String> changeVisibility(Map<String, String> idReceiptHandle, int visibilityTimeout);
+
+   /**
+    * Same as {@link #changeVisibility(Map, int)}, except that we generate
+    * numeric ids starting with {@code 1}
+    * 
+    * @param receiptHandles
+    *           receipt handles to change visibility
+    * @see #changeVisibility(Map, int)
+    */
+   BatchResult<String> changeVisibility(Iterable<String> receiptHandles, int visibilityTimeout);
+
    /**
     * The SendMessage action delivers a message to the specified queue. The
     * maximum allowed message size is 64 KB.
@@ -138,6 +247,107 @@ public interface MessageApi {
     */
    MessageIdAndMD5 send(String message);
 
+   /**
+    * Same as {@link #send(Map)} except you can set a delay for each message in
+    * the request.
+    * 
+    * <h4>Example usage</h4>
+    * 
+    * <pre>
+    * BatchResult<? extends MessageIdAndMD5> results = api.sendWithDelays(ImmutableTable.<String, String, Integer>builder()
+    *                                  .put("id1", "test message one", 1)
+    *                                  .put("id2", "test message two", 10)
+    *                                  .build());
+    * 
+    * if (results.keySet().equals(ImmutableSet.of("id", "id2"))
+    *    // all ok
+    * else
+    *   results.getErrors();
+    * </pre>
+    * 
+    * @param idMessageBodyDelaySeconds
+    *           id for correlating the result, message body, and delay seconds
+    * 
+    * @return result that contains success or errors of the operation
+    * @see #send(String, SendMessageOptions)
+    */
+   BatchResult<? extends MessageIdAndMD5> sendWithDelays(Table<String, String, Integer> idMessageBodyDelaySeconds);
+
+   /**
+    * Same as {@link #sendWithDelays(Table)}, except that we generate numeric
+    * ids starting with {@code 1}
+    * 
+    * @param messageBodyDelaySeconds
+    *           message body to the delay desired
+    * @see #sendWithDelays(Table)
+    */
+   BatchResult<? extends MessageIdAndMD5> sendWithDelays(Map<String, Integer> messageBodyDelaySeconds);
+
+   /**
+    * Same as {@link #send(Map)} except you set a delay for all messages in the
+    * request
+    * 
+    * @param delaySeconds
+    *           The number of seconds to delay a specific message. Messages with
+    *           a positive DelaySeconds value become available for processing
+    *           after the delay time is finished.
+    * 
+    * @see #send(String, SendMessageOptions)
+    */
+   BatchResult<? extends MessageIdAndMD5> sendWithDelay(Map<String, String> idMessageBody, int delaySeconds);
+
+   /**
+    * Same as {@link #sendWithDelay(Map, int)}, except that we generate numeric
+    * ids starting with {@code 1}
+    * 
+    * @param messageBodies
+    *           message bodies to send
+    * @see #sendWithDelay(Map, int)
+    */
+   BatchResult<? extends MessageIdAndMD5> sendWithDelay(Iterable<String> messageBodies, int delaySeconds);
+
+   /**
+    * The SendMessageBatch action delivers up to ten messages to the specified
+    * queue. The maximum allowed individual message size is 64 KiB (65,536
+    * bytes).
+    * 
+    * The maximum total payload size (i.e., the sum of all a batch's individual
+    * message lengths) is also 64 KiB (65,536 bytes).
+    * 
+    * Currently, you can send up to 10 {@link #send} requests.
+    * 
+    * action. <h4>Example usage</h4>
+    * 
+    * <pre>
+    * BatchResult<? extends MessageIdAndMD5> results = api.send(ImmutableMap.<String, String>builder()
+    *                                  .put("id1", "test message one")
+    *                                  .put("id2", "test message two")
+    *                                  .build());
+    * 
+    * if (results.keySet().equals(ImmutableSet.of("id", "id2"))
+    *    // all ok
+    * else
+    *   results.getErrors();
+    * </pre>
+    * 
+    * @param idMessageBody
+    *           id for correlating the result to message body
+    * 
+    * @return result that contains success or errors of the operation
+    * @see #send(String)
+    */
+   BatchResult<? extends MessageIdAndMD5> send(Map<String, String> idMessageBody);
+
+   /**
+    * Same as {@link #send(Map)}, except that we generate numeric ids starting
+    * with {@code 1}
+    * 
+    * @param messageBodies
+    *           message bodies to send
+    * @see #send(Map)
+    */
+   BatchResult<? extends MessageIdAndMD5> send(Iterable<String> messageBodies);
+
    /**
     * same as {@link #sendMessage(URI, String)} except you can control options
     * such as delay seconds.
@@ -192,7 +402,7 @@ public interface MessageApi {
     *           maximum messages to receive, current limit is 10
     * @see #receive(URI)
     */
-   Set<Message> receive(int max);
+   List<Message> receive(int max);
 
    /**
     * same as {@link #receive(URI, int)} except you can provide options like
@@ -205,5 +415,5 @@ public interface MessageApi {
     *           options such as VisibilityTimeout
     * @see #receive(URI, int)
     */
-   Set<Message> receive(int max, ReceiveMessageOptions options);
+   List<Message> receive(int max, ReceiveMessageOptions options);
 }
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/features/MessageAsyncApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/features/MessageAsyncApi.java
new file mode 100644
index 0000000000..6eb9db388d
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/features/MessageAsyncApi.java
@@ -0,0 +1,275 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import static org.jclouds.sqs.reference.SQSParameters.ACTION;
+import static org.jclouds.sqs.reference.SQSParameters.VERSION;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.jclouds.Constants;
+import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.FormParams;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.VirtualHost;
+import org.jclouds.rest.annotations.XMLResponseParser;
+import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
+import org.jclouds.sqs.binders.BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams;
+import org.jclouds.sqs.binders.BindDeleteMessageBatchRequestEntryToIndexedFormParams;
+import org.jclouds.sqs.binders.BindSendMessageBatchRequestEntryToIndexedFormParams;
+import org.jclouds.sqs.binders.BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams;
+import org.jclouds.sqs.domain.BatchResult;
+import org.jclouds.sqs.domain.Message;
+import org.jclouds.sqs.domain.MessageIdAndMD5;
+import org.jclouds.sqs.options.ReceiveMessageOptions;
+import org.jclouds.sqs.options.SendMessageOptions;
+import org.jclouds.sqs.xml.ChangeMessageVisibilityBatchResponseHandler;
+import org.jclouds.sqs.xml.DeleteMessageBatchResponseHandler;
+import org.jclouds.sqs.xml.MessageHandler;
+import org.jclouds.sqs.xml.ReceiveMessageResponseHandler;
+import org.jclouds.sqs.xml.RegexMessageIdAndMD5Handler;
+import org.jclouds.sqs.xml.SendMessageBatchResponseHandler;
+
+import com.google.common.collect.Table;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides access to SQS via their REST API.
+ * <p/>
+ * 
+ * @author Adrian Cole
+ */
+@RequestFilters(FormSigner.class)
+@FormParams(keys = VERSION, values = "{" + Constants.PROPERTY_API_VERSION + "}")
+@VirtualHost
+public interface MessageAsyncApi {
+
+   /**
+    * @see MessageApi#delete(String)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "DeleteMessage")
+   @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
+   ListenableFuture<Void> delete(@FormParam("ReceiptHandle") String receiptHandle);
+
+   /**
+    * @see MessageApi#delete(Map)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "DeleteMessageBatch")
+   @XMLResponseParser(DeleteMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<String>> delete(
+         @BinderParam(BindDeleteMessageBatchRequestEntryToIndexedFormParams.class) Map<String, String> idReceiptHandle);
+
+   /**
+    * @see MessageApi#delete(Iterable)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "DeleteMessageBatch")
+   @XMLResponseParser(DeleteMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<String>> delete(
+         @BinderParam(BindDeleteMessageBatchRequestEntryToIndexedFormParams.class) Iterable<String> receiptHandles);
+
+   /**
+    * @see MessageApi#changeVisibility(String, int)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ChangeMessageVisibility")
+   ListenableFuture<Void> changeVisibility(@FormParam("ReceiptHandle") String receiptHandle,
+         @FormParam("VisibilityTimeout") int visibilityTimeout);
+
+   /**
+    * @see MessageApi#changeVisibility(Table)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ChangeMessageVisibilityBatch")
+   @XMLResponseParser(ChangeMessageVisibilityBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<String>> changeVisibility(
+         @BinderParam(BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams.class) Table<String, String, Integer> idReceiptHandleVisibilityTimeout);
+
+   /**
+    * @see MessageApi#changeVisibility(Map)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ChangeMessageVisibilityBatch")
+   @XMLResponseParser(ChangeMessageVisibilityBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<String>> changeVisibility(
+         @BinderParam(BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams.class) Map<String, Integer> receiptHandleVisibilityTimeout);
+
+   /**
+    * @see MessageApi#changeVisibility(Map, int)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ChangeMessageVisibilityBatch")
+   @MapBinder(BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams.class)
+   @XMLResponseParser(ChangeMessageVisibilityBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<String>> changeVisibility(
+         @PayloadParam("idReceiptHandle") Map<String, String> idReceiptHandle,
+         @PayloadParam("visibilityTimeout") int visibilityTimeout);
+
+   /**
+    * @see MessageApi#changeVisibility(Iterable, int)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ChangeMessageVisibilityBatch")
+   @MapBinder(BindChangeMessageVisibilityBatchRequestEntryToIndexedFormParams.class)
+   @XMLResponseParser(ChangeMessageVisibilityBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<String>> changeVisibility(
+         @PayloadParam("receiptHandles") Iterable<String> receiptHandles,
+         @PayloadParam("visibilityTimeout") int visibilityTimeout);
+
+   /**
+    * @see MessageApi#send(String)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessage")
+   @ResponseParser(RegexMessageIdAndMD5Handler.class)
+   ListenableFuture<? extends MessageIdAndMD5> send(@FormParam("MessageBody") String message);
+
+   /**
+    * @see MessageApi#send(String, SendMessageOptions)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessage")
+   @ResponseParser(RegexMessageIdAndMD5Handler.class)
+   ListenableFuture<? extends MessageIdAndMD5> send(@FormParam("MessageBody") String message, SendMessageOptions options);
+
+   /**
+    * @see MessageApi#sendWithDelays(Table)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessageBatch")
+   @ResponseParser(RegexMessageIdAndMD5Handler.class)
+   @XMLResponseParser(SendMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<? extends MessageIdAndMD5>> sendWithDelays(
+         @BinderParam(BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams.class) Table<String, String, Integer> idMessageBodyDelaySeconds);
+
+   /**
+    * @see MessageApi#sendWithDelays(Map)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessageBatch")
+   @ResponseParser(RegexMessageIdAndMD5Handler.class)
+   @XMLResponseParser(SendMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<? extends MessageIdAndMD5>> sendWithDelays(
+         @BinderParam(BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams.class) Map<String, Integer> messageBodyDelaySeconds);
+
+   /**
+    * @see MessageApi#sendWithDelay(Map, int)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessageBatch")
+   @MapBinder(BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams.class)
+   @XMLResponseParser(SendMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<? extends MessageIdAndMD5>> sendWithDelay(
+         @PayloadParam("idMessageBody") Map<String, String> idMessageBody,
+         @PayloadParam("delaySeconds") int delaySeconds);
+
+   /**
+    * @see MessageApi#sendWithDelay(Iterable, int)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessageBatch")
+   @MapBinder(BindSendMessageBatchRequestEntryWithDelaysToIndexedFormParams.class)
+   @XMLResponseParser(SendMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<? extends MessageIdAndMD5>> sendWithDelay(
+         @PayloadParam("messageBodies") Iterable<String> messageBodies, @PayloadParam("delaySeconds") int delaySeconds);
+
+   /**
+    * @see MessageApi#send(Map)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessageBatch")
+   @XMLResponseParser(SendMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<? extends MessageIdAndMD5>> send(
+         @BinderParam(BindSendMessageBatchRequestEntryToIndexedFormParams.class) Map<String, String> idMessageBody);
+
+   /**
+    * @see MessageApi#send(Iterable)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "SendMessageBatch")
+   @XMLResponseParser(SendMessageBatchResponseHandler.class)
+   ListenableFuture<? extends BatchResult<? extends MessageIdAndMD5>> send(
+         @BinderParam(BindSendMessageBatchRequestEntryToIndexedFormParams.class) Iterable<String> messageBodies);
+
+   /**
+    * @see MessageApi#receive()
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ReceiveMessage")
+   @XMLResponseParser(MessageHandler.class)
+   ListenableFuture<Message> receive();
+
+   /**
+    * @see MessageApi#receive(ReceiveMessageOptions)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ReceiveMessage")
+   @XMLResponseParser(MessageHandler.class)
+   ListenableFuture<? extends Message> receive(ReceiveMessageOptions options);
+
+   /**
+    * @see MessageApi#receive(int)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ReceiveMessage")
+   @XMLResponseParser(ReceiveMessageResponseHandler.class)
+   ListenableFuture<? extends List<? extends Message>> receive(@FormParam("MaxNumberOfMessages") int max);
+
+   /**
+    * @see MessageApi#receive(int, ReceiveMessageOptions)
+    */
+   @POST
+   @Path("/")
+   @FormParams(keys = ACTION, values = "ReceiveMessage")
+   @XMLResponseParser(ReceiveMessageResponseHandler.class)
+   ListenableFuture<? extends List<? extends Message>> receive(@FormParam("MaxNumberOfMessages") int max,
+         ReceiveMessageOptions options);
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/PermissionApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/features/PermissionApi.java
similarity index 98%
rename from labs/sqs/src/main/java/org/jclouds/sqs/PermissionApi.java
rename to labs/sqs/src/main/java/org/jclouds/sqs/features/PermissionApi.java
index 71a2b4852b..2f2498e1a3 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/PermissionApi.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/features/PermissionApi.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.sqs;
+package org.jclouds.sqs.features;
 
 import java.util.concurrent.TimeUnit;
 
@@ -27,7 +27,7 @@ import org.jclouds.sqs.domain.Action;
  * Provides access to SQS via their REST API.
  * <p/>
  * 
- * @see SQSAsyncApi
+ * @see PermissionAsyncApi
  * @author Adrian Cole
  */
 @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/PermissionAsyncApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/features/PermissionAsyncApi.java
similarity index 94%
rename from labs/sqs/src/main/java/org/jclouds/sqs/PermissionAsyncApi.java
rename to labs/sqs/src/main/java/org/jclouds/sqs/features/PermissionAsyncApi.java
index 9eccd9b228..54902bd0ef 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/PermissionAsyncApi.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/features/PermissionAsyncApi.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.sqs;
+package org.jclouds.sqs.features;
 
 import static org.jclouds.sqs.reference.SQSParameters.ACTION;
 import static org.jclouds.sqs.reference.SQSParameters.VERSION;
@@ -46,7 +46,7 @@ import com.google.common.util.concurrent.ListenableFuture;
 public interface PermissionAsyncApi {
 
    /**
-    * @see SQSApi#addPermissionToAccount
+    * @see PermissionApi#addPermissionToAccount
     */
    @POST
    @Path("/")
@@ -55,7 +55,7 @@ public interface PermissionAsyncApi {
          @FormParam("ActionName.1") Action permission, @FormParam("AWSAccountId.1") String accountId);
 
    /**
-    * @see SQSApi#remove
+    * @see PermissionApi#remove
     */
    @POST
    @Path("/")
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/QueueApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/features/QueueApi.java
similarity index 99%
rename from labs/sqs/src/main/java/org/jclouds/sqs/QueueApi.java
rename to labs/sqs/src/main/java/org/jclouds/sqs/features/QueueApi.java
index 0492b819a0..ccd6dbec67 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/QueueApi.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/features/QueueApi.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.sqs;
+package org.jclouds.sqs.features;
 
 import java.net.URI;
 import java.util.Map;
@@ -32,7 +32,7 @@ import org.jclouds.sqs.options.ListQueuesOptions;
  * Provides access to SQS via their REST API.
  * <p/>
  * 
- * @see SQSAsyncApi
+ * @see QueueAsyncApi
  * @author Adrian Cole
  */
 @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/QueueAsyncApi.java b/labs/sqs/src/main/java/org/jclouds/sqs/features/QueueAsyncApi.java
similarity index 91%
rename from labs/sqs/src/main/java/org/jclouds/sqs/QueueAsyncApi.java
rename to labs/sqs/src/main/java/org/jclouds/sqs/features/QueueAsyncApi.java
index 4621b90dad..251cc9dec1 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/QueueAsyncApi.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/features/QueueAsyncApi.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.sqs;
+package org.jclouds.sqs.features;
 
 import static org.jclouds.sqs.reference.SQSParameters.ACTION;
 import static org.jclouds.sqs.reference.SQSParameters.VERSION;
@@ -41,6 +41,7 @@ import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.annotations.VirtualHost;
 import org.jclouds.rest.annotations.XMLResponseParser;
 import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
 import org.jclouds.sqs.binders.BindAttributeNamesToIndexedFormParams;
 import org.jclouds.sqs.domain.QueueAttributes;
 import org.jclouds.sqs.functions.MapToQueueAttributes;
@@ -65,7 +66,7 @@ import com.google.common.util.concurrent.ListenableFuture;
 public interface QueueAsyncApi {
 
    /**
-    * @see SQSApi#list
+    * @see QueueApi#list
     */
    @POST
    @Path("/")
@@ -74,7 +75,7 @@ public interface QueueAsyncApi {
    ListenableFuture<Set<URI>> list();
 
    /**
-    * @see SQSApi#list
+    * @see QueueApi#list(ListQueuesOptions)
     */
    @POST
    @Path("/")
@@ -83,7 +84,7 @@ public interface QueueAsyncApi {
    ListenableFuture<Set<URI>> list(ListQueuesOptions options);
 
    /**
-    * @see SQSApi#create
+    * @see QueueApi#create
     */
    @POST
    @Path("/")
@@ -92,7 +93,7 @@ public interface QueueAsyncApi {
    ListenableFuture<URI> create(@FormParam("QueueName") String queueName);
 
    /**
-    * @see SQSApi#create
+    * @see QueueApi#create
     */
    @POST
    @Path("/")
@@ -101,15 +102,16 @@ public interface QueueAsyncApi {
    ListenableFuture<URI> create(@FormParam("QueueName") String queueName, CreateQueueOptions options);
 
    /**
-    * @see SQSApi#delete
+    * @see QueueApi#delete
     */
    @POST
    @Path("/")
    @FormParams(keys = ACTION, values = "DeleteQueue")
+   @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
    ListenableFuture<Void> delete(@EndpointParam URI queue);
 
    /**
-    * @see SQSApi#getAttributes(URI)
+    * @see QueueApi#getAttributes(URI)
     */
    @POST
    @Path("/")
@@ -120,7 +122,7 @@ public interface QueueAsyncApi {
    ListenableFuture<? extends QueueAttributes> getAttributes(@EndpointParam URI queue);
 
    /**
-    * @see SQSApi#getAttributes(URI, Iterable)
+    * @see QueueApi#getAttributes(URI, Iterable)
     */
    @POST
    @Path("/")
@@ -130,7 +132,7 @@ public interface QueueAsyncApi {
          @BinderParam(BindAttributeNamesToIndexedFormParams.class) Iterable<String> attributeNames);
 
    /**
-    * @see SQSApi#getAttribute
+    * @see QueueApi#getAttribute
     */
    @POST
    @Path("/")
@@ -139,7 +141,7 @@ public interface QueueAsyncApi {
    ListenableFuture<String> getAttribute(@EndpointParam URI queue, @FormParam("AttributeName.1") String attributeName);
 
    /**
-    * @see SQSApi#setAttribute
+    * @see QueueApi#setAttribute
     */
    @POST
    @Path("/")
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/BatchErrorHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/BatchErrorHandler.java
new file mode 100644
index 0000000000..24889e29e6
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/BatchErrorHandler.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.sqs.domain.BatchError;
+
+/**
+ * @see <a href=
+ *      "http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QueryDeleteMessageBatch.html"
+ *      />
+ * 
+ * @author Adrian Cole
+ */
+public class BatchErrorHandler extends ParseSax.HandlerForGeneratedRequestWithResult<BatchError> {
+
+   private StringBuilder currentText = new StringBuilder();
+   private BatchError.Builder builder = BatchError.builder();
+
+   @Override
+   public BatchError getResult() {
+      try {
+         return builder.build();
+      } catch (NullPointerException e) {
+         return null;
+      } finally {
+         builder = BatchError.builder();
+      }
+   }
+
+   @Override
+   public void endElement(String uri, String name, String qName) {
+      if (qName.equals("Id")) {
+         builder.id(currentOrNull(currentText));
+      } else if (qName.equals("SenderFault")) {
+         builder.senderFault(Boolean.parseBoolean(currentOrNull(currentText)));
+      } else if (qName.equals("Code")) {
+         builder.code(currentOrNull(currentText));
+      } else if (qName.equals("Message")) {
+         builder.message(currentOrNull(currentText));
+      }
+      currentText = new StringBuilder();
+   }
+
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/BatchResponseHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/BatchResponseHandler.java
new file mode 100644
index 0000000000..07bd366d42
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/BatchResponseHandler.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.http.functions.ParseSax.HandlerForGeneratedRequestWithResult;
+import org.jclouds.sqs.domain.BatchError;
+import org.jclouds.sqs.domain.BatchResult;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QueryDeleteMessageBatch.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class BatchResponseHandler<V> extends ParseSax.HandlerForGeneratedRequestWithResult<BatchResult<V>> {
+
+   private final String resultElement;
+   private final ParseSax.HandlerForGeneratedRequestWithResult<Map.Entry<String, V>> resultHandler;
+   private final BatchErrorHandler errorHandler;
+
+   private ImmutableMap.Builder<String, V> results = ImmutableMap.<String,V> builder();
+   private Builder<BatchError> errors = ImmutableSet.<BatchError> builder();
+
+   private boolean inResult;
+   private boolean inError;
+
+   protected BatchResponseHandler(String resultElement, HandlerForGeneratedRequestWithResult<Map.Entry<String, V>> resultHandler,
+         BatchErrorHandler errorHandler) {
+      this.resultElement = checkNotNull(resultElement, "resultElement");;
+      this.resultHandler = checkNotNull(resultHandler, "resultHandler");;
+      this.errorHandler = checkNotNull(errorHandler, "errorHandler");;
+   }
+
+   @Override
+   public BatchResult<V> getResult() {
+      return BatchResult.<V> builder().putAll(results.build()).errors(errors.build())
+            .build();
+   }
+
+   @Override
+   public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
+      if (qName.equals(resultElement)) {
+         inResult = true;
+      } else if (qName.equals("BatchResultErrorEntry")) {
+         inError = true;
+      }
+      if (inResult) {
+         resultHandler.startElement(url, name, qName, attributes);
+      } else if (inError) {
+         errorHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (qName.equals(resultElement)) {
+         results.put(resultHandler.getResult());
+         inResult = false;
+      } else if (qName.equals("BatchResultErrorEntry")) {
+         errors.add(errorHandler.getResult());
+         inError = false;
+      } else if (inResult) {
+         resultHandler.endElement(uri, name, qName);
+      } else if (inError) {
+         errorHandler.endElement(uri, name, qName);
+      }
+   }
+
+   @Override
+   public void characters(char ch[], int start, int length) throws SAXException {
+      if (inResult) {
+         resultHandler.characters(ch, start, length);
+      } else if (inError) {
+         errorHandler.characters(ch, start, length);
+      }
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/ChangeMessageVisibilityBatchResponseHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/ChangeMessageVisibilityBatchResponseHandler.java
new file mode 100644
index 0000000000..819f67158f
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/ChangeMessageVisibilityBatchResponseHandler.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import javax.inject.Inject;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QueryChangeMessageVisibilityBatch.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class ChangeMessageVisibilityBatchResponseHandler extends BatchResponseHandler<String> {
+   @Inject
+   protected ChangeMessageVisibilityBatchResponseHandler(IdHandler resultHandler, BatchErrorHandler errorHandler) {
+      super("ChangeMessageVisibilityBatchResultEntry", resultHandler, errorHandler);
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/DeleteMessageBatchResponseHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/DeleteMessageBatchResponseHandler.java
new file mode 100644
index 0000000000..1933851b15
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/DeleteMessageBatchResponseHandler.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import javax.inject.Inject;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QueryDeleteMessageBatch.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class DeleteMessageBatchResponseHandler extends BatchResponseHandler<String> {
+
+   @Inject
+   protected DeleteMessageBatchResponseHandler(IdHandler resultHandler, BatchErrorHandler errorHandler) {
+      super("DeleteMessageBatchResultEntry", resultHandler, errorHandler);
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/IdHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/IdHandler.java
new file mode 100644
index 0000000000..35d9f42191
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/IdHandler.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/**
+ * @see <a href=
+ *      "http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QueryDeleteMessageBatch.html"
+ *      />
+ * 
+ * @author Adrian Cole
+ */
+public class IdHandler extends TextFromSingleElementHandler<Map.Entry<String, String>> {
+   @Inject
+   protected IdHandler(String elementName) {
+      super("Id");
+   }
+
+   @Override
+   public Map.Entry<String, String> apply(String in) {
+      return Iterables.getOnlyElement(ImmutableMap.of(in, in).entrySet());
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/ReceiveMessageResponseHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/ReceiveMessageResponseHandler.java
index 7976476acc..76c5551add 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/xml/ReceiveMessageResponseHandler.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/ReceiveMessageResponseHandler.java
@@ -20,15 +20,15 @@ package org.jclouds.sqs.xml;
 
 import static org.jclouds.util.SaxUtils.equalsOrSuffix;
 
-import java.util.Set;
+import java.util.List;
 
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.sqs.domain.Message;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
 import com.google.inject.Inject;
 
 /**
@@ -38,11 +38,11 @@ import com.google.inject.Inject;
  * 
  * @author Adrian Cole
  */
-public class ReceiveMessageResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Set<Message>> {
+public class ReceiveMessageResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<List<Message>> {
 
    private final MessageHandler messageHandler;
 
-   private Builder<Message> messages = ImmutableSet.<Message> builder();
+   private Builder<Message> messages = ImmutableList.<Message> builder();
 
    private boolean inMessages;
 
@@ -52,7 +52,7 @@ public class ReceiveMessageResponseHandler extends ParseSax.HandlerForGeneratedR
    }
 
    @Override
-   public Set<Message> getResult() {
+   public List<Message> getResult() {
       return messages.build();
    }
 
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/SendMessageBatchResponseHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/SendMessageBatchResponseHandler.java
new file mode 100644
index 0000000000..bb7780e29b
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/SendMessageBatchResponseHandler.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import javax.inject.Inject;
+
+import org.jclouds.sqs.domain.MessageIdAndMD5;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QuerySendMessageBatch.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class SendMessageBatchResponseHandler extends BatchResponseHandler<MessageIdAndMD5> {
+
+   @Inject
+   protected SendMessageBatchResponseHandler(SendMessageBatchResultEntryHandler resultHandler,
+         BatchErrorHandler errorHandler) {
+      super("SendMessageBatchResultEntry", resultHandler, errorHandler);
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/SendMessageBatchResultEntryHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/SendMessageBatchResultEntryHandler.java
new file mode 100644
index 0000000000..0474e2e75f
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/SendMessageBatchResultEntryHandler.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.jclouds.crypto.CryptoStreams;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.sqs.domain.MessageIdAndMD5;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.hash.HashCodes;
+
+/**
+ * @see <a href=
+ *      "http://docs.amazonwebservices.com/AWSSimpleQueueService/2011-10-01/APIReference/Query_QueryReceiveMessage.html"
+ *      />
+ * 
+ * @author Adrian Cole
+ */
+public class SendMessageBatchResultEntryHandler extends
+      ParseSax.HandlerForGeneratedRequestWithResult<Map.Entry<String, MessageIdAndMD5>> {
+
+   private StringBuilder currentText = new StringBuilder();
+   private MessageIdAndMD5.Builder builder = MessageIdAndMD5.builder();
+   private String id;
+
+   @Override
+   public Entry<String, MessageIdAndMD5> getResult() {
+      try {
+         return Iterables.getOnlyElement(ImmutableMap.of(id, builder.build()).entrySet());
+      } finally {
+         builder = MessageIdAndMD5.builder();
+      }
+   }
+
+   @Override
+   public void endElement(String uri, String name, String qName) {
+      if (qName.equals("Id")) {
+         this.id = currentOrNull(currentText);
+      } else if (qName.equals("MessageId")) {
+         builder.id(currentOrNull(currentText));
+      } else if (qName.equals("MD5OfMessageBody")) {
+         builder.md5(HashCodes.fromBytes(CryptoStreams.hex(currentOrNull(currentText))));
+      }
+      currentText = new StringBuilder();
+   }
+
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/TextFromSingleElementHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/TextFromSingleElementHandler.java
new file mode 100644
index 0000000000..cfde3d14df
--- /dev/null
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/TextFromSingleElementHandler.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.xml;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.util.SaxUtils.currentOrNull;
+
+import org.jclouds.http.functions.ParseSax;
+
+import com.google.common.base.Function;
+
+/**
+ * looks for a single value in the xml
+ * 
+ * @author Adrian Cole
+ */
+public abstract class TextFromSingleElementHandler<V> extends ParseSax.HandlerForGeneratedRequestWithResult<V>
+      implements Function<String, V> {
+   private final String elementName;
+
+   protected TextFromSingleElementHandler(String elementName) {
+      this.elementName = checkNotNull(elementName, "elementName");
+   }
+
+   private StringBuilder currentText = new StringBuilder();
+   private String text;
+
+   @Override
+   public V getResult() {
+      return apply(text);
+   }
+
+   // this could be done with regex, if we had an unescaper
+   @Override
+   public void endElement(String uri, String name, String qName) {
+      if (qName.equals(elementName)) {
+         text = currentOrNull(currentText);
+      }
+      currentText = new StringBuilder();
+   }
+
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/sqs/src/main/java/org/jclouds/sqs/xml/ValueHandler.java b/labs/sqs/src/main/java/org/jclouds/sqs/xml/ValueHandler.java
index b30c622f27..c3764d3a83 100644
--- a/labs/sqs/src/main/java/org/jclouds/sqs/xml/ValueHandler.java
+++ b/labs/sqs/src/main/java/org/jclouds/sqs/xml/ValueHandler.java
@@ -18,9 +18,7 @@
  */
 package org.jclouds.sqs.xml;
 
-import static org.jclouds.util.SaxUtils.currentOrNull;
-
-import org.jclouds.http.functions.ParseSax;
+import javax.inject.Inject;
 
 /**
  * @see <a href=
@@ -29,28 +27,15 @@ import org.jclouds.http.functions.ParseSax;
  * 
  * @author Adrian Cole
  */
-public class ValueHandler extends ParseSax.HandlerForGeneratedRequestWithResult<String> {
-
-   private StringBuilder currentText = new StringBuilder();
-   private String value;
-
-   @Override
-   public String getResult() {
-      return value;
-   }
-
-   // this could be done with regex, if we had an unescaper
-   @Override
-   public void endElement(String uri, String name, String qName) {
-      if (qName.equals("Value")) {
-         value = currentOrNull(currentText);
-      }
-      currentText = new StringBuilder();
+public class ValueHandler extends TextFromSingleElementHandler<String> {
+   @Inject
+   protected ValueHandler(String elementName) {
+      super("Value");
    }
 
    @Override
-   public void characters(char ch[], int start, int length) {
-      currentText.append(ch, start, length);
+   public String apply(String in) {
+      return in;
    }
 
 }
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/SQSApiExpectTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/SQSApiExpectTest.java
deleted file mode 100644
index 24567740b8..0000000000
--- a/labs/sqs/src/test/java/org/jclouds/sqs/SQSApiExpectTest.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.sqs;
-
-import static org.testng.Assert.assertEquals;
-
-import java.net.URI;
-
-import org.jclouds.http.HttpRequest;
-import org.jclouds.http.HttpResponse;
-import org.jclouds.sqs.domain.Action;
-import org.jclouds.sqs.functions.MapToQueueAttributesTest;
-import org.jclouds.sqs.internal.BaseSQSApiExpectTest;
-import org.jclouds.sqs.parse.CreateQueueResponseTest;
-import org.jclouds.sqs.parse.GetQueueAttributesResponseTest;
-import org.jclouds.sqs.parse.ReceiveMessageResponseTest;
-import org.jclouds.sqs.parse.SendMessageResponseTest;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-
-/**
- * @author Adrian Cole
- */
-@Test(groups = "unit", testName = "SQSApiExpectTest")
-public class SQSApiExpectTest extends BaseSQSApiExpectTest {
-   
-   URI queue = URI.create("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/");
-   
-   public HttpRequest createQueue = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "CreateQueue")
-         .addFormParam("QueueName", "queueName")
-         .addFormParam("Signature", "I7tmwiCzJ9cvw79pmlz1rOILh2C2ZV6OpLk23JGx6AU%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testCreateQueueWhenResponseIs2xx() throws Exception {
-      
-      HttpResponse createQueueResponse = HttpResponse.builder().statusCode(200)
-            .payload(payloadFromResourceWithContentType("/create_queue.xml", "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(createQueue, createQueueResponse);
-
-      assertEquals(apiWhenExist.getQueueApi().create("queueName").toString(), new CreateQueueResponseTest().expected()
-            .toString());
-   }
-   
-   public HttpRequest sendMessage = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "SendMessage")
-         .addFormParam("MessageBody", "hardyharhar")
-         .addFormParam("Signature", "PVzszzgIcT1xt9%2BEzGzWB2Bt8zDadBc48HsgF89AoJE%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testSendMessageWhenResponseIs2xx() throws Exception {
-      
-      HttpResponse sendMessageResponse = HttpResponse.builder().statusCode(200)
-            .payload(payloadFromResourceWithContentType("/send_message.xml", "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(sendMessage, sendMessageResponse);
-
-      assertEquals(apiWhenExist.getMessageApiForQueue(queue).send("hardyharhar").toString(),
-            new SendMessageResponseTest().expected().toString());
-   }
-   
-
-   public HttpRequest receiveMessage = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "ReceiveMessage")
-         .addFormParam("Signature", "UURXsAjggoaz5P1h2EFswRd8Ji9euHmXhHvrAmIqM1E%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testReceiveMessageWhenResponseIs2xx() throws Exception {
-      
-      HttpResponse receiveMessageResponse = HttpResponse.builder().statusCode(200)
-            .payload(payloadFromResourceWithContentType("/messages.xml", "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(receiveMessage, receiveMessageResponse);
-
-      assertEquals(apiWhenExist.getMessageApiForQueue(queue).receive().toString(),
-            Iterables.get(new ReceiveMessageResponseTest().expected(), 0).toString());
-   }
-   
-
-   public HttpRequest receiveMessages = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "ReceiveMessage")
-         .addFormParam("MaxNumberOfMessages", "10")
-         .addFormParam("Signature", "pZ9B4%2BTBvQA4n0joP4t8ue5x0xmKMd9prpVLVoT%2F7qU%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testReceiveMessagesWhenResponseIs2xx() throws Exception {
-      
-      HttpResponse receiveMessagesResponse = HttpResponse.builder().statusCode(200)
-            .payload(payloadFromResourceWithContentType("/messages.xml", "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(receiveMessages, receiveMessagesResponse);
-
-      assertEquals(apiWhenExist.getMessageApiForQueue(queue).receive(10).toString(), new ReceiveMessageResponseTest()
-            .expected().toString());
-   }
-   
-   public HttpRequest deleteMessage = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "DeleteMessage")
-         .addFormParam("ReceiptHandle", "eXJYhj5rDr9cAe")
-         .addFormParam("Signature", "9%2FkuCc2i78gMsmul%2BRsOPcdQ1OLUKrItqgGIRRBJb8M%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testDeleteMessageWhenResponseIs2xx() throws Exception {
-
-      HttpResponse deleteMessageResponse = HttpResponse.builder()
-            .statusCode(200)
-            .payload(
-                  payloadFromStringWithContentType(
-                        "<DeleteMessageResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></DeleteMessageResponse>",
-                        "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(deleteMessage, deleteMessageResponse);
-
-      apiWhenExist.getMessageApiForQueue(queue).delete("eXJYhj5rDr9cAe");
-   }
-   
-   
-   public HttpRequest changeMessageVisibility = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "ChangeMessageVisibility")
-         .addFormParam("ReceiptHandle", "eXJYhj5rDr9cAe")
-         .addFormParam("Signature", "gvmSHleGLkmszYU6aURCBImuec2k0O3pg3tAYhDvkNs%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("VisibilityTimeout", "10")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testChangeMessageVisibilityWhenResponseIs2xx() throws Exception {
-
-      HttpResponse changeMessageVisibilityResponse = HttpResponse.builder()
-            .statusCode(200)
-            .payload(
-                  payloadFromStringWithContentType(
-                        "<ChangeMessageVisibilityResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></ChangeMessageVisibilityResponse>",
-                        "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(changeMessageVisibility, changeMessageVisibilityResponse);
-
-      apiWhenExist.getMessageApiForQueue(queue).changeVisibility("eXJYhj5rDr9cAe", 10);
-   }
-
-   public HttpRequest getQueueAttribute = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "GetQueueAttributes")
-         .addFormParam("AttributeName.1", "VisibilityTimeout")
-         .addFormParam("Signature", "AfydayBBaIk4UGikHHY1CFNmOOAcTnogpFWydZyNass%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testGetQueueAttributeWhenResponseIs2xx() throws Exception {
-      
-      HttpResponse getQueueAttributeResponse = HttpResponse.builder()
-            .statusCode(200)
-            .payload(
-                  payloadFromStringWithContentType(
-                        "<GetQueueAttributesResponse><GetQueueAttributesResult><Attribute><Name>VisibilityTimeout</Name><Value>30</Value></Attribute></GetQueueAttributesResult></GetQueueAttributesResponse>",
-                        "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(getQueueAttribute, getQueueAttributeResponse);
-
-      assertEquals(apiWhenExist.getQueueApi().getAttribute(queue, "VisibilityTimeout"), "30");
-   }
-   
-   public HttpRequest getQueueAttributes = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "GetQueueAttributes")
-         .addFormParam("AttributeName.1", "All")
-         .addFormParam("Signature", "welFLn0TV6JlH6s6s60XZTJeJfFXGiXN4qNPrBx7aHc%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testGetQueueAttributesWhenResponseIs2xx() throws Exception {
-      
-      HttpResponse getQueueAttributesResponse = HttpResponse.builder().statusCode(200)
-            .payload(payloadFromResourceWithContentType("/attributes.xml", "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(getQueueAttributes, getQueueAttributesResponse);
-
-      assertEquals(apiWhenExist.getQueueApi().getAttributes(queue).toString(), new MapToQueueAttributesTest()
-            .expected().toString());
-   }
-   
-   public HttpRequest getQueueAttributesSubset = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "GetQueueAttributes")
-         .addFormParam("AttributeName.1", "VisibilityTimeout")
-         .addFormParam("AttributeName.2", "DelaySeconds")
-         .addFormParam("Signature", "9KaiOOWWyFPTVMOnyHA3ZoXbPBPSD4AZ4q460UNMfDs%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testGetQueueAttributesSubsetWhenResponseIs2xx() throws Exception {
-      
-      HttpResponse getQueueAttributesSubsetResponse = HttpResponse.builder().statusCode(200)
-            .payload(payloadFromResourceWithContentType("/attributes.xml", "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(getQueueAttributesSubset, getQueueAttributesSubsetResponse);
-
-      assertEquals(apiWhenExist.getQueueApi()
-            .getAttributes(queue, ImmutableSet.of("VisibilityTimeout", "DelaySeconds")).toString(),
-            new GetQueueAttributesResponseTest().expected().toString());
-   }
-   
-   public HttpRequest setQueueAttribute = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "SetQueueAttributes")
-         .addFormParam("Attribute.Name", "MaximumMessageSize")
-         .addFormParam("Attribute.Value", "1")
-         .addFormParam("Signature", "ktBkQ3c%2FrwGcBSec0fkckfo73xmcoTuub5fxudM1qh0%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testSetQueueAttributeWhenResponseIs2xx() throws Exception {
-
-      HttpResponse setQueueAttributeResponse = HttpResponse.builder()
-            .statusCode(200)
-            .payload(
-                  payloadFromStringWithContentType(
-                        "<SetQueueAttributesResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></SetQueueAttributesResponse>",
-                        "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(setQueueAttribute, setQueueAttributeResponse);
-
-      apiWhenExist.getQueueApi().setAttribute(queue, "MaximumMessageSize", "1");
-   }
-   
-   public HttpRequest addPermission = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "AddPermission")
-         .addFormParam("ActionName.1", "ReceiveMessage")
-         .addFormParam("AWSAccountId.1", "125074342641")
-         .addFormParam("Label", "testLabel")
-         .addFormParam("Signature", "J9sV4q1rJ7dWYJDQp9JxsfEKNXQhpQBYIwBYi1IeXV0%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testAddPermissionWhenResponseIs2xx() throws Exception {
-
-      HttpResponse addPermissionResponse = HttpResponse.builder()
-            .statusCode(200)
-            .payload(
-                  payloadFromStringWithContentType(
-                        "<AddPermissionsResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></AddPermissionsResponse>",
-                        "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(addPermission, addPermissionResponse);
-
-      apiWhenExist.getPermissionApiForQueue(queue).addPermissionToAccount("testLabel", Action.RECEIVE_MESSAGE, "125074342641");
-   }
-   
-   public HttpRequest removePermission = HttpRequest.builder()
-         .method("POST")
-         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
-         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
-         .addFormParam("Action", "RemovePermission")
-         .addFormParam("Label", "testLabel")
-         .addFormParam("Signature", "VOA0L1uRVKQDQL1Klt0cYUajGoxN4Ur%2B7ISQ2I4RpRs%3D")
-         .addFormParam("SignatureMethod", "HmacSHA256")
-         .addFormParam("SignatureVersion", "2")
-         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
-         .addFormParam("Version", "2011-10-01")
-         .addFormParam("AWSAccessKeyId", "identity").build();
-
-   public void testRemovePermissionWhenResponseIs2xx() throws Exception {
-
-      HttpResponse removePermissionResponse = HttpResponse.builder()
-            .statusCode(200)
-            .payload(
-                  payloadFromStringWithContentType(
-                        "<RemovePermissionsResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></RemovePermissionsResponse>",
-                        "text/xml")).build();
-
-      SQSApi apiWhenExist = requestSendsResponse(removePermission, removePermissionResponse);
-
-      apiWhenExist.getPermissionApiForQueue(queue).remove("testLabel");
-   }
-}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/SQSApiLiveTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/SQSApiLiveTest.java
deleted file mode 100644
index 200c89b0d2..0000000000
--- a/labs/sqs/src/test/java/org/jclouds/sqs/SQSApiLiveTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.sqs;
-
-import static com.google.common.collect.Iterables.get;
-import static com.google.common.collect.Iterables.getLast;
-import static org.jclouds.concurrent.MoreExecutors.sameThreadExecutor;
-import static org.jclouds.providers.AnonymousProviderMetadata.forClientMappedToAsyncClientOnEndpoint;
-import static org.jclouds.sqs.options.ListQueuesOptions.Builder.queuePrefix;
-import static org.jclouds.sqs.options.ReceiveMessageOptions.Builder.attribute;
-import static org.jclouds.sqs.reference.SQSParameters.ACTION;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-
-import java.net.URI;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-
-import org.jclouds.ContextBuilder;
-import org.jclouds.concurrent.Timeout;
-import org.jclouds.concurrent.config.ExecutorServiceModule;
-import org.jclouds.rest.annotations.FormParams;
-import org.jclouds.rest.annotations.XMLResponseParser;
-import org.jclouds.sqs.domain.Action;
-import org.jclouds.sqs.domain.QueueAttributes;
-import org.jclouds.sqs.internal.BaseSQSApiLiveTest;
-import org.jclouds.sqs.xml.ValueHandler;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.Test;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.common.hash.HashCode;
-import com.google.common.hash.Hashing;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.inject.Module;
-
-/**
- * Tests behavior of {@code SQSApi}
- * 
- * @author Adrian Cole
- */
-@Test(groups = "live", singleThreaded = true, testName = "SQSApiLiveTest")
-public class SQSApiLiveTest extends BaseSQSApiLiveTest {
-
-   private Set<URI> queues = Sets.newHashSet();
-
-   @Test
-   protected void testListQueues() throws InterruptedException {
-      listQueuesInRegion(null);
-   }
-
-   protected void listQueuesInRegion(String region) throws InterruptedException {
-      Set<URI> allResults = api().getQueueApiForRegion(region).list();
-      assertNotNull(allResults);
-      if (allResults.size() >= 1) {
-         URI queue = getLast(allResults);
-         assertQueueInList(region, queue);
-      }
-   }
-
-   public static final String PREFIX = System.getProperty("user.name") + "-sqs";
-
-   @Test
-   protected void testCanRecreateQueueGracefully() throws InterruptedException {
-      recreateQueueInRegion(PREFIX + "1", null);
-      recreateQueueInRegion(PREFIX + "1", null);
-   }
-
-   public String recreateQueueInRegion(String queueName, String region) throws InterruptedException {
-      QueueApi api = api().getQueueApiForRegion(region);
-      Set<URI> result = api.list(queuePrefix(queueName));
-      if (result.size() >= 1) {
-         api.delete(getLast(result));
-      }
-      URI queue = api.create(queueName);
-      assertQueueInList(region, queue);
-      queues.add(queue);
-      return queueName;
-   }
-
-   @Test(dependsOnMethods = "testCanRecreateQueueGracefully")
-   protected void testGetQueueAttributes() {
-      for (URI queue : queues) {
-         Map<String, String> attributes = api().getQueueApi().getAttributes(queue, ImmutableSet.of("All"));
-         assertEquals(api().getQueueApi().getAttributes(queue, attributes.keySet()), attributes);
-      }
-   }
-
-   String message = "hardyharhar";
-   HashCode md5 = Hashing.md5().hashString(message, Charsets.UTF_8);
-
-   @Timeout(duration = 5, timeUnit = TimeUnit.SECONDS)
-   static interface AnonymousAttributesApi {
-      String getQueueArn();
-   }
-
-   static interface AnonymousAttributesAsyncApi {
-      @POST
-      @Path("/")
-      @FormParams(keys = { ACTION, "AttributeName.1" }, values = { "GetQueueAttributes", "QueueArn" })
-      @XMLResponseParser(ValueHandler.class)
-      ListenableFuture<String> getQueueArn();
-   }
-
-   @Test(dependsOnMethods = "testGetQueueAttributes")
-   protected void testAddAnonymousPermission() throws InterruptedException {
-      for (URI queue : queues) {
-         QueueAttributes attributes = api().getQueueApi().getAttributes(queue);
-         assertNoPermissions(queue);
-
-         String accountToAuthorize = getAccountToAuthorize(queue);
-         api().getPermissionApiForQueue(queue).addPermissionToAccount("fubar", Action.GET_QUEUE_ATTRIBUTES,
-               accountToAuthorize);
-
-         String policyForAuthorizationByAccount = assertPolicyPresent(queue);
-
-         String policyForAnonymous = policyForAuthorizationByAccount.replace("\"" + accountToAuthorize + "\"", "\"*\"");
-         api().getQueueApi().setAttribute(queue, "Policy", policyForAnonymous);
-
-         assertEquals(getAnonymousAttributesApi(queue).getQueueArn(), attributes.getQueueArn());
-      }
-   }
-
-   protected String getAccountToAuthorize(URI queue) {
-      return get(Splitter.on('/').split(queue.getPath()), 1);
-   }
-
-   @Test(dependsOnMethods = "testAddAnonymousPermission")
-   protected void testRemovePermission() throws InterruptedException {
-      for (URI queue : queues) {
-         api().getPermissionApiForQueue(queue).remove("fubar");
-         assertNoPermissions(queue);
-      }
-   }
-
-   @Test(dependsOnMethods = "testGetQueueAttributes")
-   protected void testSetQueueAttribute() {
-      for (URI queue : queues) {
-         api().getQueueApi().setAttribute(queue, "MaximumMessageSize", "1024");
-         assertEquals(api().getQueueApi().getAttributes(queue).getMaximumMessageSize(), 1024);
-      }
-   }
-
-   @Test(dependsOnMethods = "testGetQueueAttributes")
-   protected void testSendMessage() {
-      for (URI queue : queues) {
-         assertEquals(api().getMessageApiForQueue(queue).send(message).getMD5(), md5);
-      }
-   }
-
-   @Test(dependsOnMethods = "testSendMessage")
-   protected void testReceiveMessageWithoutHidingMessage() {
-      for (URI queue : queues) {
-         assertEquals(api().getMessageApiForQueue(queue).receive(attribute("All").visibilityTimeout(0)).getMD5(), md5);
-      }
-   }
-
-   String receiptHandle;
-
-   @Test(dependsOnMethods = "testReceiveMessageWithoutHidingMessage")
-   protected void testChangeMessageVisibility() {
-      for (URI queue : queues) {
-         MessageApi api = api().getMessageApiForQueue(queue);
-         // start hiding it at 5 seconds
-         receiptHandle = api.receive(attribute("None").visibilityTimeout(5)).getReceiptHandle();
-         // hidden message, so we can't see it
-         assertNull(api.receive());
-         // this should unhide it
-         api.changeVisibility(receiptHandle, 0);
-         // so we can see it again
-         assertEquals(api.receive(attribute("All").visibilityTimeout(0)).getMD5(), md5);
-      }
-   }
-
-   @Test(dependsOnMethods = "testChangeMessageVisibility")
-   protected void testDeleteMessage() throws InterruptedException {
-      for (URI queue : queues) {
-         api().getMessageApiForQueue(queue).delete(receiptHandle);
-         assertNoMessages(queue);
-      }
-   }
-
-   @Override
-   @AfterClass(groups = "live")
-   protected void tearDownContext() {
-      for (URI queue : queues) {
-         api().getQueueApi().delete(queue);
-      }
-      super.tearDownContext();
-   }
-
-   protected SQSApi api() {
-      return context.getApi();
-   }
-
-   private AnonymousAttributesApi getAnonymousAttributesApi(URI queue) {
-      return ContextBuilder
-            .newBuilder(
-                  forClientMappedToAsyncClientOnEndpoint(AnonymousAttributesApi.class,
-                        AnonymousAttributesAsyncApi.class, queue.toASCIIString()))
-            .modules(ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor())))
-            .buildInjector().getInstance(AnonymousAttributesApi.class);
-   }
-
-}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/features/BulkMessageApiLiveTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/features/BulkMessageApiLiveTest.java
new file mode 100644
index 0000000000..7bb8376b7e
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/features/BulkMessageApiLiveTest.java
@@ -0,0 +1,130 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import static org.jclouds.sqs.options.ReceiveMessageOptions.Builder.attribute;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URI;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.jclouds.sqs.domain.BatchResult;
+import org.jclouds.sqs.domain.Message;
+import org.jclouds.sqs.domain.MessageIdAndMD5;
+import org.jclouds.sqs.internal.BaseSQSApiLiveTest;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.internal.annotations.Sets;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Iterables;
+import com.google.common.hash.Hashing;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "BulkMessageApiLiveTest")
+public class BulkMessageApiLiveTest extends BaseSQSApiLiveTest {
+
+   private ImmutableMap<String, String> idPayload;
+
+   public BulkMessageApiLiveTest() {
+      prefix = prefix + "-bulk";
+
+      Builder<String, String> builder = ImmutableMap.<String, String> builder();
+      for (int i = 0; i < 10; i++) {
+         String message = "hardyharhar" + i;
+         builder.put(i + "", message);
+      }
+      idPayload = builder.build();
+   }
+
+   @BeforeClass(groups = { "integration", "live" })
+   @Override
+   public void setupContext() {
+      super.setupContext();
+      recreateQueueInRegion(prefix, null);
+   }
+
+   public void testSendMessages() {
+      for (URI queue : queues) {
+         BatchResult<? extends MessageIdAndMD5> acks = api().getMessageApiForQueue(queue).send(idPayload);
+
+         assertEquals(acks.size(), idPayload.size(), "error sending " + acks);
+         assertEquals(acks.keySet(), idPayload.keySet());
+
+         for (Entry<String, ? extends MessageIdAndMD5> entry : acks.entrySet()) {
+            assertEquals(entry.getValue().getMD5(),
+                  Hashing.md5().hashString(idPayload.get(entry.getKey()), Charsets.UTF_8), "bad md5 for: " + entry);
+         }
+      }
+   }
+
+   private Iterable<String> receiptHandles;
+
+   @Test(dependsOnMethods = "testSendMessages")
+   public void testChangeMessageVisibility() {
+      for (URI queue : queues) {
+         MessageApi api = api().getMessageApiForQueue(queue);
+         
+         Set<Message> messages = collectMessages(api);
+
+         receiptHandles = Iterables.transform(messages, new Function<Message, String>() {
+            @Override
+            public String apply(Message in) {
+               return in.getReceiptHandle();
+            }
+         });
+
+         // hidden message, so we can't see it
+         assertNull(api.receive());
+
+         // this should unhide it
+         BatchResult<String> acks = api.changeVisibility(receiptHandles, 0);
+         assertEquals(acks.size(), messages.size(), "error changing visibility " + acks);
+
+         // so we can see it again
+         assertEquals(collectMessages(api).size(), messages.size());
+      }
+   }
+
+   protected Set<Message> collectMessages(MessageApi api) {
+      // you are not guaranteed to get all messages in the same request
+      Set<Message> messages = Sets.newLinkedHashSet();
+      while (messages.size() != idPayload.size())
+         messages.addAll(api.receive(idPayload.size(), attribute("None").visibilityTimeout(5)));
+      return messages;
+   }
+
+   @Test(dependsOnMethods = "testChangeMessageVisibility")
+   public void testDeleteMessage() throws InterruptedException {
+      for (URI queue : queues) {
+         BatchResult<String> acks = api().getMessageApiForQueue(queue).delete(receiptHandles);
+         assertEquals(acks.size(), Iterables.size(receiptHandles), "error deleting messages " + acks);
+         assertNoMessages(queue);
+      }
+   }
+
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/features/MessageApiExpectTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/features/MessageApiExpectTest.java
new file mode 100644
index 0000000000..da2fa1b138
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/features/MessageApiExpectTest.java
@@ -0,0 +1,542 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.sqs.SQSApi;
+import org.jclouds.sqs.internal.BaseSQSApiExpectTest;
+import org.jclouds.sqs.parse.ChangeMessageVisibilityBatchResponseTest;
+import org.jclouds.sqs.parse.DeleteMessageBatchResponseTest;
+import org.jclouds.sqs.parse.ReceiveMessageResponseTest;
+import org.jclouds.sqs.parse.SendMessageBatchResponseTest;
+import org.jclouds.sqs.parse.SendMessageResponseTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "MessageApiExpectTest")
+public class MessageApiExpectTest extends BaseSQSApiExpectTest {
+   
+   public HttpRequest sendMessage = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SendMessage")
+         .addFormParam("MessageBody", "hardyharhar")
+         .addFormParam("Signature", "PVzszzgIcT1xt9%2BEzGzWB2Bt8zDadBc48HsgF89AoJE%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testSendMessageWhenResponseIs2xx() throws Exception {
+      
+      HttpResponse sendMessageResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/send_message.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(sendMessage, sendMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).send("hardyharhar").toString(),
+            new SendMessageResponseTest().expected().toString());
+   }
+   
+   public HttpRequest sendMessageIterable = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SendMessageBatch")
+         .addFormParam("SendMessageBatchRequestEntry.1.Id", "1")
+         .addFormParam("SendMessageBatchRequestEntry.1.MessageBody", "payload1")
+         .addFormParam("SendMessageBatchRequestEntry.2.Id", "2")
+         .addFormParam("SendMessageBatchRequestEntry.2.MessageBody", "payload2")
+         .addFormParam("Signature", "2AYMDMLhoLncALJgBfHBGfOkaTB5ut3PeFRJeWffxdI%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testSendMessageIterableWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/send_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(sendMessageIterable, sendMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).send(ImmutableSet.of("payload1", "payload2"))
+            .toString(), new SendMessageBatchResponseTest().expected().toString());
+   }
+
+   public HttpRequest sendMessageMap = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SendMessageBatch")
+         .addFormParam("SendMessageBatchRequestEntry.1.Id", "foo1")
+         .addFormParam("SendMessageBatchRequestEntry.1.MessageBody", "payload1")
+         .addFormParam("SendMessageBatchRequestEntry.2.Id", "foo2")
+         .addFormParam("SendMessageBatchRequestEntry.2.MessageBody", "payload2")
+         .addFormParam("Signature", "f9v8e%2FrPXTI3zhBYMhg7U8yCfvPqHjAV8bFjhGL6%2BXc%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+   
+   public void testSendMessageMapWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/send_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(sendMessageMap, sendMessageResponse);
+
+      assertEquals(
+            apiWhenExist.getMessageApiForQueue(queue)
+                  .send(ImmutableMap.of("foo1", "payload1", "foo2", "payload2")).toString(),
+            new SendMessageBatchResponseTest().expected().toString());
+   }
+   
+   public HttpRequest sendMessageWithDelayMap = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SendMessageBatch")
+         .addFormParam("SendMessageBatchRequestEntry.1.DelaySeconds", "10")
+         .addFormParam("SendMessageBatchRequestEntry.1.Id", "foo1")
+         .addFormParam("SendMessageBatchRequestEntry.1.MessageBody", "payload1")
+         .addFormParam("SendMessageBatchRequestEntry.2.DelaySeconds", "10")
+         .addFormParam("SendMessageBatchRequestEntry.2.Id", "foo2")
+         .addFormParam("SendMessageBatchRequestEntry.2.MessageBody", "payload2")
+         .addFormParam("Signature", "COjjEaJ76EwziEFtkT2FuSRSbrCIu%2FhlJf1Zmu7cYoU%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testSendMessageWithDelayMapWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/send_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(sendMessageWithDelayMap, sendMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).sendWithDelay(ImmutableMap.<String, String>builder()
+            .put("foo1", "payload1")
+            .put("foo2", "payload2")
+            .build(), 10)
+            .toString(), new SendMessageBatchResponseTest().expected().toString());
+   }
+
+   public HttpRequest sendMessageWithDelayIterable = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SendMessageBatch")
+         .addFormParam("SendMessageBatchRequestEntry.1.DelaySeconds", "10")
+         .addFormParam("SendMessageBatchRequestEntry.1.Id", "1")
+         .addFormParam("SendMessageBatchRequestEntry.1.MessageBody", "payload1")
+         .addFormParam("SendMessageBatchRequestEntry.2.DelaySeconds", "10")
+         .addFormParam("SendMessageBatchRequestEntry.2.Id", "2")
+         .addFormParam("SendMessageBatchRequestEntry.2.MessageBody", "payload2")
+         .addFormParam("Signature", "8AVNvSVXPSnoXjJAc6h1rysMBBZPnSycbnmD2%2FqpdV8%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+   
+   public void testSendMessageWithDelayIterableWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/send_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(sendMessageWithDelayIterable, sendMessageResponse);
+
+      assertEquals(
+            apiWhenExist.getMessageApiForQueue(queue).sendWithDelay(ImmutableSet.of("payload1", "payload2"), 10)
+                  .toString(), new SendMessageBatchResponseTest().expected().toString());
+   }
+   public HttpRequest sendMessageWithDelaysTable = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SendMessageBatch")
+         .addFormParam("SendMessageBatchRequestEntry.1.DelaySeconds", "1")
+         .addFormParam("SendMessageBatchRequestEntry.1.Id", "foo1")
+         .addFormParam("SendMessageBatchRequestEntry.1.MessageBody", "payload1")
+         .addFormParam("SendMessageBatchRequestEntry.2.DelaySeconds", "10")
+         .addFormParam("SendMessageBatchRequestEntry.2.Id", "foo2")
+         .addFormParam("SendMessageBatchRequestEntry.2.MessageBody", "payload2")
+         .addFormParam("Signature", "M2X8Al%2BbyyDM%2B9kdN28rMn1yJWl78hJ5i4GnaMZ1sYg%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testSendMessageWithDelaysTableWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/send_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(sendMessageWithDelaysTable, sendMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).sendWithDelays(ImmutableTable.<String, String, Integer>builder()
+            .put("foo1", "payload1", 1)
+            .put("foo2", "payload2", 10)
+            .build())
+            .toString(), new SendMessageBatchResponseTest().expected().toString());
+   }
+
+   public HttpRequest sendMessageWithDelaysMap = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SendMessageBatch")
+         .addFormParam("SendMessageBatchRequestEntry.1.DelaySeconds", "1")
+         .addFormParam("SendMessageBatchRequestEntry.1.Id", "1")
+         .addFormParam("SendMessageBatchRequestEntry.1.MessageBody", "payload1")
+         .addFormParam("SendMessageBatchRequestEntry.2.DelaySeconds", "10")
+         .addFormParam("SendMessageBatchRequestEntry.2.Id", "2")
+         .addFormParam("SendMessageBatchRequestEntry.2.MessageBody", "payload2")
+         .addFormParam("Signature", "nbA4UnKDAuQCiCcvQHH%2F1UjMMeo2s3d94A27Q3t9SlI%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+   
+   public void testSendMessageWithDelaysMapWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/send_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(sendMessageWithDelaysMap, sendMessageResponse);
+
+      assertEquals(
+            apiWhenExist.getMessageApiForQueue(queue).sendWithDelays(ImmutableMap.of("payload1", 1, "payload2", 10))
+                  .toString(), new SendMessageBatchResponseTest().expected().toString());
+   }
+   
+   public HttpRequest receiveMessage = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "ReceiveMessage")
+         .addFormParam("Signature", "UURXsAjggoaz5P1h2EFswRd8Ji9euHmXhHvrAmIqM1E%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testReceiveMessageWhenResponseIs2xx() throws Exception {
+      
+      HttpResponse receiveMessageResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/messages.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(receiveMessage, receiveMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).receive().toString(),
+            Iterables.get(new ReceiveMessageResponseTest().expected(), 0).toString());
+   }
+   
+
+   public HttpRequest receiveMessages = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "ReceiveMessage")
+         .addFormParam("MaxNumberOfMessages", "10")
+         .addFormParam("Signature", "pZ9B4%2BTBvQA4n0joP4t8ue5x0xmKMd9prpVLVoT%2F7qU%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testReceiveMessagesWhenResponseIs2xx() throws Exception {
+      
+      HttpResponse receiveMessagesResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/messages.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(receiveMessages, receiveMessagesResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).receive(10).toString(), new ReceiveMessageResponseTest()
+            .expected().toString());
+   }
+   
+   public HttpRequest deleteMessage = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "DeleteMessage")
+         .addFormParam("ReceiptHandle", "eXJYhj5rDr9cAe")
+         .addFormParam("Signature", "9%2FkuCc2i78gMsmul%2BRsOPcdQ1OLUKrItqgGIRRBJb8M%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testDeleteMessageWhenResponseIs2xx() throws Exception {
+
+      HttpResponse deleteMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(
+                  payloadFromStringWithContentType(
+                        "<DeleteMessageResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></DeleteMessageResponse>",
+                        "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(deleteMessage, deleteMessageResponse);
+
+      apiWhenExist.getMessageApiForQueue(queue).delete("eXJYhj5rDr9cAe");
+   }
+
+   public HttpRequest deleteMessageIterable = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "DeleteMessageBatch")
+         .addFormParam("DeleteMessageBatchRequestEntry.1.Id", "1")
+         .addFormParam("DeleteMessageBatchRequestEntry.1.ReceiptHandle", "eXJYhj5rDr9cAe")
+         .addFormParam("DeleteMessageBatchRequestEntry.2.Id", "2")
+         .addFormParam("DeleteMessageBatchRequestEntry.2.ReceiptHandle", "fffeeerrr")
+         .addFormParam("Signature", "S4xIobjm3LOkJvibeI2X54nxKJw9r1a5zj%2FdvHlfDMY%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testDeleteMessageIterableWhenResponseIs2xx() throws Exception {
+
+      HttpResponse deleteMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/delete_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(deleteMessageIterable, deleteMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).delete(ImmutableSet.of("eXJYhj5rDr9cAe", "fffeeerrr"))
+            .toString(), new DeleteMessageBatchResponseTest().expected().toString());
+   }
+
+   public HttpRequest deleteMessageMap = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "DeleteMessageBatch")
+         .addFormParam("DeleteMessageBatchRequestEntry.1.Id", "foo1")
+         .addFormParam("DeleteMessageBatchRequestEntry.1.ReceiptHandle", "eXJYhj5rDr9cAe")
+         .addFormParam("DeleteMessageBatchRequestEntry.2.Id", "foo2")
+         .addFormParam("DeleteMessageBatchRequestEntry.2.ReceiptHandle", "fffeeerrr")
+         .addFormParam("Signature", "kwHC3F3ZoJvfibhZWVTeIwFHUzoaVMR4OViyJbsmuV0%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testDeleteMessageMapWhenResponseIs2xx() throws Exception {
+
+      HttpResponse deleteMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/delete_message_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(deleteMessageMap, deleteMessageResponse);
+
+      assertEquals(
+            apiWhenExist.getMessageApiForQueue(queue)
+                  .delete(ImmutableMap.of("foo1", "eXJYhj5rDr9cAe", "foo2", "fffeeerrr")).toString(),
+            new DeleteMessageBatchResponseTest().expected().toString());
+   }
+   
+   public HttpRequest changeMessageVisibility = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "ChangeMessageVisibility")
+         .addFormParam("ReceiptHandle", "eXJYhj5rDr9cAe")
+         .addFormParam("Signature", "gvmSHleGLkmszYU6aURCBImuec2k0O3pg3tAYhDvkNs%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("VisibilityTimeout", "10")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testChangeMessageVisibilityWhenResponseIs2xx() throws Exception {
+
+      HttpResponse changeMessageVisibilityResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(
+                  payloadFromStringWithContentType(
+                        "<ChangeMessageVisibilityResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></ChangeMessageVisibilityResponse>",
+                        "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(changeMessageVisibility, changeMessageVisibilityResponse);
+
+      apiWhenExist.getMessageApiForQueue(queue).changeVisibility("eXJYhj5rDr9cAe", 10);
+   }
+
+   public HttpRequest changeMessageVisibilityTable = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "ChangeMessageVisibilityBatch")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.Id", "foo1")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.ReceiptHandle", "aaaaaaaaa")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.VisibilityTimeout", "1")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.Id", "foo2")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.ReceiptHandle", "bbbbbbbbb")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.VisibilityTimeout", "10")
+         .addFormParam("Signature", "KjDusYiiC3hTdy3ZxLwBRHryrNoNaFb2AHJqUDu3mtQ%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testChangeMessageVisibilityTableWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/change_message_visibility_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(changeMessageVisibilityTable, sendMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).changeVisibility(ImmutableTable.<String, String, Integer>builder()
+            .put("foo1", "aaaaaaaaa", 1)
+            .put("foo2", "bbbbbbbbb", 10)
+            .build())
+            .toString(), new ChangeMessageVisibilityBatchResponseTest().expected().toString());
+   }
+
+   public HttpRequest changeMessageVisibilityMap = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "ChangeMessageVisibilityBatch")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.Id", "1")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.ReceiptHandle", "aaaaaaaaa")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.VisibilityTimeout", "1")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.Id", "2")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.ReceiptHandle", "bbbbbbbbb")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.VisibilityTimeout", "10")
+         .addFormParam("Signature", "zj2cftkpHtiYb9iOjPR3AhcVhoobi0JvOy22PvQJtho%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+   
+   public void testChangeMessageVisibilityMapWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/change_message_visibility_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(changeMessageVisibilityMap, sendMessageResponse);
+
+      assertEquals(
+            apiWhenExist.getMessageApiForQueue(queue).changeVisibility(ImmutableMap.of("aaaaaaaaa", 1, "bbbbbbbbb", 10))
+                  .toString(), new ChangeMessageVisibilityBatchResponseTest().expected().toString());
+   }
+
+   public HttpRequest changeMessageVisibilityMapInt = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "ChangeMessageVisibilityBatch")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.Id", "foo1")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.ReceiptHandle", "aaaaaaaaa")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.VisibilityTimeout", "10")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.Id", "foo2")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.ReceiptHandle", "bbbbbbbbb")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.VisibilityTimeout", "10")
+         .addFormParam("Signature", "y%2FgaaxoE5wrG2P7NIAyfDo7DTgRx2PLJUi9%2FzNnWQ6A%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testChangeMessageVisibilityMapIntWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/change_message_visibility_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(changeMessageVisibilityMapInt, sendMessageResponse);
+
+      assertEquals(apiWhenExist.getMessageApiForQueue(queue).changeVisibility(ImmutableMap.<String, String>builder()
+            .put("foo1", "aaaaaaaaa")
+            .put("foo2", "bbbbbbbbb")
+            .build(), 10)
+            .toString(), new ChangeMessageVisibilityBatchResponseTest().expected().toString());
+   }
+
+   public HttpRequest changeMessageVisibilityIterableInt = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "ChangeMessageVisibilityBatch")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.Id", "1")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.ReceiptHandle", "aaaaaaaaa")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.1.VisibilityTimeout", "10")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.Id", "2")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.ReceiptHandle", "bbbbbbbbb")
+         .addFormParam("ChangeMessageVisibilityBatchRequestEntry.2.VisibilityTimeout", "10")
+         .addFormParam("Signature", "f5aq7zdKFErM3%2BIdtDX5NOzPO7mqCRzPGj2wUUEWjgE%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+   
+   public void testChangeMessageVisibilityIterableIntWhenResponseIs2xx() throws Exception {
+
+      HttpResponse sendMessageResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResourceWithContentType("/change_message_visibility_batch.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(changeMessageVisibilityIterableInt, sendMessageResponse);
+
+      assertEquals(
+            apiWhenExist.getMessageApiForQueue(queue).changeVisibility(ImmutableSet.of("aaaaaaaaa", "bbbbbbbbb"), 10)
+                  .toString(), new ChangeMessageVisibilityBatchResponseTest().expected().toString());
+   }
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/features/MessageApiLiveTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/features/MessageApiLiveTest.java
new file mode 100644
index 0000000000..0e2a3ea77c
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/features/MessageApiLiveTest.java
@@ -0,0 +1,94 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import static org.jclouds.sqs.options.ReceiveMessageOptions.Builder.attribute;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URI;
+
+import org.jclouds.sqs.internal.BaseSQSApiLiveTest;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hashing;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "MessageApiLiveTest")
+public class MessageApiLiveTest extends BaseSQSApiLiveTest {
+
+   public MessageApiLiveTest() {
+      prefix = prefix + "-message";
+   }
+
+   @BeforeClass(groups = { "integration", "live" })
+   @Override
+   public void setupContext() {
+      super.setupContext();
+      recreateQueueInRegion(prefix, null);
+   }
+
+   String message = "hardyharhar";
+   HashCode md5 = Hashing.md5().hashString(message, Charsets.UTF_8);
+
+   public void testSendMessage() {
+      for (URI queue : queues) {
+         assertEquals(api().getMessageApiForQueue(queue).send(message).getMD5(), md5);
+      }
+   }
+
+   @Test(dependsOnMethods = "testSendMessage")
+   public void testReceiveMessageWithoutHidingMessage() {
+      for (URI queue : queues) {
+         assertEquals(api().getMessageApiForQueue(queue).receive(attribute("All").visibilityTimeout(0)).getMD5(), md5);
+      }
+   }
+
+   String receiptHandle;
+
+   @Test(dependsOnMethods = "testReceiveMessageWithoutHidingMessage")
+   public void testChangeMessageVisibility() {
+      for (URI queue : queues) {
+         MessageApi api = api().getMessageApiForQueue(queue);
+         // start hiding it at 5 seconds
+         receiptHandle = api.receive(attribute("None").visibilityTimeout(5)).getReceiptHandle();
+         // hidden message, so we can't see it
+         assertNull(api.receive());
+         // this should unhide it
+         api.changeVisibility(receiptHandle, 0);
+         // so we can see it again
+         assertEquals(api.receive(attribute("All").visibilityTimeout(0)).getMD5(), md5);
+      }
+   }
+
+   @Test(dependsOnMethods = "testChangeMessageVisibility")
+   public void testDeleteMessage() throws InterruptedException {
+      for (URI queue : queues) {
+         api().getMessageApiForQueue(queue).delete(receiptHandle);
+         assertNoMessages(queue);
+      }
+   }
+
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/features/PermissionApiExpectTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/features/PermissionApiExpectTest.java
new file mode 100644
index 0000000000..037de11b3a
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/features/PermissionApiExpectTest.java
@@ -0,0 +1,89 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.sqs.SQSApi;
+import org.jclouds.sqs.domain.Action;
+import org.jclouds.sqs.internal.BaseSQSApiExpectTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "PermissionApiExpectTest")
+public class PermissionApiExpectTest extends BaseSQSApiExpectTest {
+   
+   public HttpRequest addPermission = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "AddPermission")
+         .addFormParam("ActionName.1", "ReceiveMessage")
+         .addFormParam("AWSAccountId.1", "125074342641")
+         .addFormParam("Label", "testLabel")
+         .addFormParam("Signature", "J9sV4q1rJ7dWYJDQp9JxsfEKNXQhpQBYIwBYi1IeXV0%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testAddPermissionWhenResponseIs2xx() throws Exception {
+
+      HttpResponse addPermissionResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(
+                  payloadFromStringWithContentType(
+                        "<AddPermissionsResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></AddPermissionsResponse>",
+                        "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(addPermission, addPermissionResponse);
+
+      apiWhenExist.getPermissionApiForQueue(queue).addPermissionToAccount("testLabel", Action.RECEIVE_MESSAGE, "125074342641");
+   }
+   
+   public HttpRequest removePermission = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "RemovePermission")
+         .addFormParam("Label", "testLabel")
+         .addFormParam("Signature", "VOA0L1uRVKQDQL1Klt0cYUajGoxN4Ur%2B7ISQ2I4RpRs%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testRemovePermissionWhenResponseIs2xx() throws Exception {
+
+      HttpResponse removePermissionResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(
+                  payloadFromStringWithContentType(
+                        "<RemovePermissionsResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></RemovePermissionsResponse>",
+                        "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(removePermission, removePermissionResponse);
+
+      apiWhenExist.getPermissionApiForQueue(queue).remove("testLabel");
+   }
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/features/PermissionApiLiveTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/features/PermissionApiLiveTest.java
new file mode 100644
index 0000000000..b1aa629840
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/features/PermissionApiLiveTest.java
@@ -0,0 +1,121 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import static com.google.common.collect.Iterables.get;
+import static org.jclouds.concurrent.MoreExecutors.sameThreadExecutor;
+import static org.jclouds.providers.AnonymousProviderMetadata.forClientMappedToAsyncClientOnEndpoint;
+import static org.jclouds.sqs.reference.SQSParameters.ACTION;
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.rest.annotations.FormParams;
+import org.jclouds.rest.annotations.XMLResponseParser;
+import org.jclouds.sqs.domain.Action;
+import org.jclouds.sqs.domain.QueueAttributes;
+import org.jclouds.sqs.internal.BaseSQSApiLiveTest;
+import org.jclouds.sqs.xml.ValueHandler;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.inject.Module;
+
+/**
+ * 
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "PermissionApiLiveTest")
+public class PermissionApiLiveTest extends BaseSQSApiLiveTest {
+
+   public PermissionApiLiveTest() {
+      prefix = prefix + "-permission";
+   }
+
+   @BeforeClass(groups = { "integration", "live" })
+   @Override
+   public void setupContext() {
+      super.setupContext();
+      recreateQueueInRegion(prefix, null);
+   }
+
+   @Timeout(duration = 5, timeUnit = TimeUnit.SECONDS)
+   static interface AnonymousAttributesApi {
+      String getQueueArn();
+   }
+
+   static interface AnonymousAttributesAsyncApi {
+      @POST
+      @Path("/")
+      @FormParams(keys = { ACTION, "AttributeName.1" }, values = { "GetQueueAttributes", "QueueArn" })
+      @XMLResponseParser(ValueHandler.class)
+      ListenableFuture<String> getQueueArn();
+   }
+
+   public void testAddAnonymousPermission() throws InterruptedException {
+      for (URI queue : queues) {
+         QueueAttributes attributes = api().getQueueApi().getAttributes(queue);
+         assertNoPermissions(queue);
+
+         String accountToAuthorize = getAccountToAuthorize(queue);
+         api().getPermissionApiForQueue(queue).addPermissionToAccount("fubar", Action.GET_QUEUE_ATTRIBUTES,
+               accountToAuthorize);
+
+         String policyForAuthorizationByAccount = assertPolicyPresent(queue);
+
+         String policyForAnonymous = policyForAuthorizationByAccount.replace("\"" + accountToAuthorize + "\"", "\"*\"");
+         api().getQueueApi().setAttribute(queue, "Policy", policyForAnonymous);
+
+         assertEquals(getAnonymousAttributesApi(queue).getQueueArn(), attributes.getQueueArn());
+      }
+   }
+
+   protected String getAccountToAuthorize(URI queue) {
+      return get(Splitter.on('/').split(queue.getPath()), 1);
+   }
+
+   @Test(dependsOnMethods = "testAddAnonymousPermission")
+   public void testRemovePermission() throws InterruptedException {
+      for (URI queue : queues) {
+         api().getPermissionApiForQueue(queue).remove("fubar");
+         assertNoPermissions(queue);
+      }
+   }
+
+   private AnonymousAttributesApi getAnonymousAttributesApi(URI queue) {
+      return ContextBuilder
+            .newBuilder(
+                  forClientMappedToAsyncClientOnEndpoint(AnonymousAttributesApi.class,
+                        AnonymousAttributesAsyncApi.class, queue.toASCIIString()))
+            .modules(ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor())))
+            .buildInjector().getInstance(AnonymousAttributesApi.class);
+   }
+
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/features/QueueApiExpectTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/features/QueueApiExpectTest.java
new file mode 100644
index 0000000000..a9b09a1c4f
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/features/QueueApiExpectTest.java
@@ -0,0 +1,169 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.sqs.SQSApi;
+import org.jclouds.sqs.functions.MapToQueueAttributesTest;
+import org.jclouds.sqs.internal.BaseSQSApiExpectTest;
+import org.jclouds.sqs.parse.CreateQueueResponseTest;
+import org.jclouds.sqs.parse.GetQueueAttributesResponseTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "QueueApiExpectTest")
+public class QueueApiExpectTest extends BaseSQSApiExpectTest {
+   
+   public HttpRequest createQueue = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "CreateQueue")
+         .addFormParam("QueueName", "queueName")
+         .addFormParam("Signature", "I7tmwiCzJ9cvw79pmlz1rOILh2C2ZV6OpLk23JGx6AU%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testCreateQueueWhenResponseIs2xx() throws Exception {
+      
+      HttpResponse createQueueResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/create_queue.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(createQueue, createQueueResponse);
+
+      assertEquals(apiWhenExist.getQueueApi().create("queueName").toString(), new CreateQueueResponseTest().expected()
+            .toString());
+   }
+   
+   public HttpRequest getQueueAttribute = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "GetQueueAttributes")
+         .addFormParam("AttributeName.1", "VisibilityTimeout")
+         .addFormParam("Signature", "AfydayBBaIk4UGikHHY1CFNmOOAcTnogpFWydZyNass%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testGetQueueAttributeWhenResponseIs2xx() throws Exception {
+      
+      HttpResponse getQueueAttributeResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(
+                  payloadFromStringWithContentType(
+                        "<GetQueueAttributesResponse><GetQueueAttributesResult><Attribute><Name>VisibilityTimeout</Name><Value>30</Value></Attribute></GetQueueAttributesResult></GetQueueAttributesResponse>",
+                        "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(getQueueAttribute, getQueueAttributeResponse);
+
+      assertEquals(apiWhenExist.getQueueApi().getAttribute(queue, "VisibilityTimeout"), "30");
+   }
+   
+   public HttpRequest getQueueAttributes = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "GetQueueAttributes")
+         .addFormParam("AttributeName.1", "All")
+         .addFormParam("Signature", "welFLn0TV6JlH6s6s60XZTJeJfFXGiXN4qNPrBx7aHc%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testGetQueueAttributesWhenResponseIs2xx() throws Exception {
+      
+      HttpResponse getQueueAttributesResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/attributes.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(getQueueAttributes, getQueueAttributesResponse);
+
+      assertEquals(apiWhenExist.getQueueApi().getAttributes(queue).toString(), new MapToQueueAttributesTest()
+            .expected().toString());
+   }
+   
+   public HttpRequest getQueueAttributesSubset = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "GetQueueAttributes")
+         .addFormParam("AttributeName.1", "VisibilityTimeout")
+         .addFormParam("AttributeName.2", "DelaySeconds")
+         .addFormParam("Signature", "9KaiOOWWyFPTVMOnyHA3ZoXbPBPSD4AZ4q460UNMfDs%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testGetQueueAttributesSubsetWhenResponseIs2xx() throws Exception {
+      
+      HttpResponse getQueueAttributesSubsetResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/attributes.xml", "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(getQueueAttributesSubset, getQueueAttributesSubsetResponse);
+
+      assertEquals(apiWhenExist.getQueueApi()
+            .getAttributes(queue, ImmutableSet.of("VisibilityTimeout", "DelaySeconds")).toString(),
+            new GetQueueAttributesResponseTest().expected().toString());
+   }
+   
+   public HttpRequest setQueueAttribute = HttpRequest.builder()
+         .method("POST")
+         .endpoint("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/")
+         .addHeader("Host", "sqs.us-east-1.amazonaws.com")
+         .addFormParam("Action", "SetQueueAttributes")
+         .addFormParam("Attribute.Name", "MaximumMessageSize")
+         .addFormParam("Attribute.Value", "1")
+         .addFormParam("Signature", "ktBkQ3c%2FrwGcBSec0fkckfo73xmcoTuub5fxudM1qh0%3D")
+         .addFormParam("SignatureMethod", "HmacSHA256")
+         .addFormParam("SignatureVersion", "2")
+         .addFormParam("Timestamp", "2009-11-08T15%3A54%3A08.897Z")
+         .addFormParam("Version", "2011-10-01")
+         .addFormParam("AWSAccessKeyId", "identity").build();
+
+   public void testSetQueueAttributeWhenResponseIs2xx() throws Exception {
+
+      HttpResponse setQueueAttributeResponse = HttpResponse.builder()
+            .statusCode(200)
+            .payload(
+                  payloadFromStringWithContentType(
+                        "<SetQueueAttributesResponse><ResponseMetadata><RequestId>b5293cb5-d306-4a17-9048-b263635abe42</RequestId></ResponseMetadata></SetQueueAttributesResponse>",
+                        "text/xml")).build();
+
+      SQSApi apiWhenExist = requestSendsResponse(setQueueAttribute, setQueueAttributeResponse);
+
+      apiWhenExist.getQueueApi().setAttribute(queue, "MaximumMessageSize", "1");
+   }
+
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/features/QueueApiLiveTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/features/QueueApiLiveTest.java
new file mode 100644
index 0000000000..d6481ece9a
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/features/QueueApiLiveTest.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.features;
+
+import static com.google.common.collect.Iterables.getLast;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.sqs.internal.BaseSQSApiLiveTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "QueueApiLiveTest")
+public class QueueApiLiveTest extends BaseSQSApiLiveTest {
+
+   public QueueApiLiveTest() {
+      prefix = prefix + "-queue";
+   }
+
+   @Test
+   public void testListQueues() throws InterruptedException {
+      listQueuesInRegion(null);
+   }
+
+   protected void listQueuesInRegion(String region) throws InterruptedException {
+      Set<URI> allResults = api().getQueueApiForRegion(region).list();
+      assertNotNull(allResults);
+      if (allResults.size() >= 1) {
+         URI queue = getLast(allResults);
+         assertQueueInList(region, queue);
+      }
+   }
+
+   @Test
+   public void testCanRecreateQueueGracefully() throws InterruptedException {
+      recreateQueueInRegion(prefix, null);
+      recreateQueueInRegion(prefix, null);
+   }
+
+   @Test(dependsOnMethods = "testCanRecreateQueueGracefully")
+   public void testGetQueueAttributes() {
+      for (URI queue : queues) {
+         Map<String, String> attributes = api().getQueueApi().getAttributes(queue, ImmutableSet.of("All"));
+         assertEquals(api().getQueueApi().getAttributes(queue, attributes.keySet()), attributes);
+      }
+   }
+
+   @Test(dependsOnMethods = "testGetQueueAttributes")
+   public void testSetQueueAttribute() {
+      for (URI queue : queues) {
+         api().getQueueApi().setAttribute(queue, "MaximumMessageSize", "1024");
+         assertEquals(api().getQueueApi().getAttributes(queue).getMaximumMessageSize(), 1024);
+      }
+   }
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSApiLiveTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSApiLiveTest.java
index 7b60c8df48..35799ec397 100644
--- a/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSApiLiveTest.java
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSApiLiveTest.java
@@ -18,12 +18,15 @@
  */
 package org.jclouds.sqs.internal;
 
+import static com.google.common.collect.Iterables.getLast;
+import static org.jclouds.sqs.options.ListQueuesOptions.Builder.queuePrefix;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import java.net.URI;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.jclouds.apis.BaseContextLiveTest;
@@ -32,9 +35,13 @@ import org.jclouds.sqs.SQSApi;
 import org.jclouds.sqs.SQSApiMetadata;
 import org.jclouds.sqs.SQSAsyncApi;
 import org.jclouds.sqs.domain.Message;
+import org.jclouds.sqs.features.QueueApi;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.Sets;
 import com.google.common.reflect.TypeToken;
+import com.google.common.util.concurrent.Uninterruptibles;
 
 /**
  * 
@@ -43,16 +50,32 @@ import com.google.common.reflect.TypeToken;
 @Test(groups = "live")
 public class BaseSQSApiLiveTest extends BaseContextLiveTest<RestContext<SQSApi, SQSAsyncApi>> {
 
+   protected String prefix = System.getProperty("user.name") + "-sqs";
+
    public BaseSQSApiLiveTest() {
       provider = "sqs";
    }
 
+   protected Set<URI> queues = Sets.newHashSet();
+
+   protected String recreateQueueInRegion(String queueName, String region) {
+      QueueApi api = api().getQueueApiForRegion(region);
+      Set<URI> result = api.list(queuePrefix(queueName));
+      if (result.size() >= 1) {
+         api.delete(getLast(result));
+      }
+      URI queue = api.create(queueName);
+      assertQueueInList(region, queue);
+      queues.add(queue);
+      return queueName;
+   }
+
    @Override
    protected TypeToken<RestContext<SQSApi, SQSAsyncApi>> contextType() {
       return SQSApiMetadata.CONTEXT_TOKEN;
    }
 
-   protected String assertPolicyPresent(final URI queue) throws InterruptedException {
+   protected String assertPolicyPresent(final URI queue) {
       final AtomicReference<String> policy = new AtomicReference<String>();
       assertEventually(new Runnable() {
          public void run() {
@@ -65,7 +88,7 @@ public class BaseSQSApiLiveTest extends BaseContextLiveTest<RestContext<SQSApi,
       return policy.get();
    }
 
-   protected void assertNoPermissions(final URI queue) throws InterruptedException {
+   protected void assertNoPermissions(final URI queue) {
       assertEventually(new Runnable() {
          public void run() {
             String policy = api().getQueueApi().getAttribute(queue, "Policy");
@@ -74,7 +97,7 @@ public class BaseSQSApiLiveTest extends BaseContextLiveTest<RestContext<SQSApi,
       });
    }
 
-   protected void assertNoMessages(final URI queue) throws InterruptedException {
+   protected void assertNoMessages(final URI queue) {
       assertEventually(new Runnable() {
          public void run() {
             Message message = api().getMessageApiForQueue(queue).receive();
@@ -83,7 +106,7 @@ public class BaseSQSApiLiveTest extends BaseContextLiveTest<RestContext<SQSApi,
       });
    }
 
-   protected void assertQueueInList(final String region, URI queue) throws InterruptedException {
+   protected void assertQueueInList(final String region, URI queue) {
       final URI finalQ = queue;
       assertEventually(new Runnable() {
          public void run() {
@@ -95,10 +118,6 @@ public class BaseSQSApiLiveTest extends BaseContextLiveTest<RestContext<SQSApi,
       });
    }
 
-   private SQSApi api() {
-      return context.getApi();
-   }
-
    private static final int INCONSISTENCY_WINDOW = 10000;
 
    /**
@@ -106,7 +125,7 @@ public class BaseSQSApiLiveTest extends BaseContextLiveTest<RestContext<SQSApi,
     * immediately. Hence, we will try up to the inconsistency window to see if
     * the assertion completes.
     */
-   protected static void assertEventually(Runnable assertion) throws InterruptedException {
+   protected static void assertEventually(Runnable assertion) {
       long start = System.currentTimeMillis();
       AssertionError error = null;
       for (int i = 0; i < 30; i++) {
@@ -119,9 +138,23 @@ public class BaseSQSApiLiveTest extends BaseContextLiveTest<RestContext<SQSApi,
          } catch (AssertionError e) {
             error = e;
          }
-         Thread.sleep(INCONSISTENCY_WINDOW / 30);
+         Uninterruptibles.sleepUninterruptibly(INCONSISTENCY_WINDOW / 30, TimeUnit.MILLISECONDS);
       }
       if (error != null)
          throw error;
    }
+
+   @Override
+   @AfterClass(groups = "live")
+   protected void tearDownContext() {
+      for (URI queue : queues) {
+         api().getQueueApi().delete(queue);
+      }
+      super.tearDownContext();
+   }
+
+   protected SQSApi api() {
+      return context.getApi();
+   }
+
 }
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSExpectTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSExpectTest.java
index 6896a1ad65..082cb1d2b4 100644
--- a/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSExpectTest.java
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/internal/BaseSQSExpectTest.java
@@ -18,6 +18,8 @@
  */
 package org.jclouds.sqs.internal;
 
+import java.net.URI;
+
 import org.jclouds.date.DateService;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.internal.BaseRestApiExpectTest;
@@ -30,6 +32,7 @@ import com.google.inject.Module;
  * @author Adrian Cole
  */
 public class BaseSQSExpectTest<T> extends BaseRestApiExpectTest<T> {
+   protected URI queue = URI.create("https://sqs.us-east-1.amazonaws.com/993194456877/adrian-sqs11/");
 
    public BaseSQSExpectTest() {
       provider = "sqs";
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/parse/ChangeMessageVisibilityBatchResponseTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/parse/ChangeMessageVisibilityBatchResponseTest.java
new file mode 100644
index 0000000000..4e40613310
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/parse/ChangeMessageVisibilityBatchResponseTest.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.sqs.domain.BatchResult;
+import org.jclouds.sqs.xml.ChangeMessageVisibilityBatchResponseHandler;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during
+// surefire
+@Test(groups = "unit", testName = "ChangeMessageVisibilityBatchResponseTest")
+public class ChangeMessageVisibilityBatchResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/change_message_visibility_batch.xml");
+
+      BatchResult<String> expected = expected();
+
+      ChangeMessageVisibilityBatchResponseHandler handler = injector.getInstance(ChangeMessageVisibilityBatchResponseHandler.class);
+      BatchResult<String> result = factory.create(handler).parse(is);
+
+      assertEquals(result.toString(), expected.toString());
+
+   }
+
+   public BatchResult<String> expected() {
+      return BatchResult.<String> builder()
+            .put("change_visibility_msg_2","change_visibility_msg_2")
+            .put("change_visibility_msg_3","change_visibility_msg_3")
+            .build();
+   }
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/parse/DeleteMessageBatchResponseTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/parse/DeleteMessageBatchResponseTest.java
new file mode 100644
index 0000000000..c7e4ce47c5
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/parse/DeleteMessageBatchResponseTest.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.sqs.domain.BatchResult;
+import org.jclouds.sqs.xml.DeleteMessageBatchResponseHandler;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during
+// surefire
+@Test(groups = "unit", testName = "DeleteMessageBatchResponseTest")
+public class DeleteMessageBatchResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/delete_message_batch.xml");
+
+      BatchResult<String> expected = expected();
+
+      DeleteMessageBatchResponseHandler handler = injector.getInstance(DeleteMessageBatchResponseHandler.class);
+      BatchResult<String> result = factory.create(handler).parse(is);
+
+      assertEquals(result.toString(), expected.toString());
+
+   }
+
+   public BatchResult<String> expected() {
+      return BatchResult.<String> builder()
+            .put("msg1","msg1")
+            .put("msg2","msg2")
+            .build();
+   }
+}
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/parse/ReceiveMessageResponseTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/parse/ReceiveMessageResponseTest.java
index d345656efd..77f50ee0b1 100644
--- a/labs/sqs/src/test/java/org/jclouds/sqs/parse/ReceiveMessageResponseTest.java
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/parse/ReceiveMessageResponseTest.java
@@ -21,7 +21,7 @@ package org.jclouds.sqs.parse;
 import static org.testng.Assert.assertEquals;
 
 import java.io.InputStream;
-import java.util.Set;
+import java.util.List;
 
 import org.jclouds.crypto.CryptoStreams;
 import org.jclouds.http.functions.BaseHandlerTest;
@@ -29,7 +29,7 @@ import org.jclouds.sqs.domain.Message;
 import org.jclouds.sqs.xml.ReceiveMessageResponseHandler;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableList;
 import com.google.common.hash.HashCodes;
 
 /**
@@ -43,17 +43,17 @@ public class ReceiveMessageResponseTest extends BaseHandlerTest {
    public void test() {
       InputStream is = getClass().getResourceAsStream("/messages.xml");
 
-      Set<Message> expected = expected();
+      List<Message> expected = expected();
 
       ReceiveMessageResponseHandler handler = injector.getInstance(ReceiveMessageResponseHandler.class);
-      Set<Message> result = factory.create(handler).parse(is);
+      List<Message> result = factory.create(handler).parse(is);
 
       assertEquals(result.toString(), expected.toString());
 
    }
 
-   public Set<Message> expected() {
-      return ImmutableSet.of(Message
+   public List<Message> expected() {
+      return ImmutableList.of(Message
             .builder()
             .id("5fea7756-0ea4-451a-a703-a558b933e274")
             .receiptHandle(
diff --git a/labs/sqs/src/test/java/org/jclouds/sqs/parse/SendMessageBatchResponseTest.java b/labs/sqs/src/test/java/org/jclouds/sqs/parse/SendMessageBatchResponseTest.java
new file mode 100644
index 0000000000..fcafc10c10
--- /dev/null
+++ b/labs/sqs/src/test/java/org/jclouds/sqs/parse/SendMessageBatchResponseTest.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.sqs.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.crypto.CryptoStreams;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.sqs.domain.BatchResult;
+import org.jclouds.sqs.domain.MessageIdAndMD5;
+import org.jclouds.sqs.xml.SendMessageBatchResponseHandler;
+import org.testng.annotations.Test;
+
+import com.google.common.hash.HashCodes;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during
+// surefire
+@Test(groups = "unit", testName = "SendMessageBatchResponseTest")
+public class SendMessageBatchResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/send_message_batch.xml");
+
+      BatchResult<MessageIdAndMD5> expected = expected();
+
+      SendMessageBatchResponseHandler handler = injector.getInstance(SendMessageBatchResponseHandler.class);
+      BatchResult<MessageIdAndMD5> result = factory.create(handler).parse(is);
+
+      assertEquals(result.toString(), expected.toString());
+
+   }
+
+   public BatchResult<MessageIdAndMD5> expected() {
+      return BatchResult
+            .<MessageIdAndMD5> builder()
+            .put("test_msg_001",
+                  MessageIdAndMD5.builder().id("0a5231c7-8bff-4955-be2e-8dc7c50a25fa")
+                        .md5(HashCodes.fromBytes(CryptoStreams.hex("0e024d309850c78cba5eabbeff7cae71"))).build())
+            .put("test_msg_002",
+                  MessageIdAndMD5.builder().id("15ee1ed3-87e7-40c1-bdaa-2e49968ea7e9")
+                        .md5(HashCodes.fromBytes(CryptoStreams.hex("7fb8146a82f95e0af155278f406862c2"))).build())
+            .build();
+   }
+}
diff --git a/labs/sqs/src/test/resources/change_message_visibility_batch.xml b/labs/sqs/src/test/resources/change_message_visibility_batch.xml
new file mode 100644
index 0000000000..bfc4707d34
--- /dev/null
+++ b/labs/sqs/src/test/resources/change_message_visibility_batch.xml
@@ -0,0 +1,13 @@
+<ChangeMessageVisibilityBatchResponse>
+    <ChangeMessageVisibilityBatchResult>
+        <ChangeMessageVisibilityBatchResultEntry>
+            <Id>change_visibility_msg_2</Id>
+        </ChangeMessageVisibilityBatchResultEntry>
+        <ChangeMessageVisibilityBatchResultEntry>
+            <Id>change_visibility_msg_3</Id>
+        </ChangeMessageVisibilityBatchResultEntry>
+    </ChangeMessageVisibilityBatchResult>
+    <ResponseMetadata>
+        <RequestId>ca9668f7-ab1b-4f7a-8859-f15747ab17a7</RequestId>
+    </ResponseMetadata>
+</ChangeMessageVisibilityBatchResponse>
\ No newline at end of file
diff --git a/labs/sqs/src/test/resources/delete_message_batch.xml b/labs/sqs/src/test/resources/delete_message_batch.xml
new file mode 100644
index 0000000000..aeaefa7f5e
--- /dev/null
+++ b/labs/sqs/src/test/resources/delete_message_batch.xml
@@ -0,0 +1,13 @@
+<DeleteMessageBatchResponse>
+    <DeleteMessageBatchResult>
+        <DeleteMessageBatchResultEntry>
+            <Id>msg1</Id>
+        </DeleteMessageBatchResultEntry>
+        <DeleteMessageBatchResultEntry>
+            <Id>msg2</Id>
+        </DeleteMessageBatchResultEntry>
+    </DeleteMessageBatchResult>
+    <ResponseMetadata>
+        <RequestId>d6f86b7a-74d1-4439-b43f-196a1e29cd85</RequestId>
+    </ResponseMetadata>
+</DeleteMessageBatchResponse>
\ No newline at end of file
diff --git a/labs/sqs/src/test/resources/send_message_batch.xml b/labs/sqs/src/test/resources/send_message_batch.xml
new file mode 100644
index 0000000000..5761d08ea9
--- /dev/null
+++ b/labs/sqs/src/test/resources/send_message_batch.xml
@@ -0,0 +1,17 @@
+<SendMessageBatchResponse>
+	<SendMessageBatchResult>
+		<SendMessageBatchResultEntry>
+			<Id>test_msg_001</Id>
+			<MessageId>0a5231c7-8bff-4955-be2e-8dc7c50a25fa</MessageId>
+			<MD5OfMessageBody>0e024d309850c78cba5eabbeff7cae71</MD5OfMessageBody>
+		</SendMessageBatchResultEntry>
+		<SendMessageBatchResultEntry>
+			<Id>test_msg_002</Id>
+			<MessageId>15ee1ed3-87e7-40c1-bdaa-2e49968ea7e9</MessageId>
+			<MD5OfMessageBody>7fb8146a82f95e0af155278f406862c2</MD5OfMessageBody>
+		</SendMessageBatchResultEntry>
+	</SendMessageBatchResult>
+	<ResponseMetadata>
+		<RequestId>ca1ad5d0-8271-408b-8d0f-1351bf547e74</RequestId>
+	</ResponseMetadata>
+</SendMessageBatchResponse>
\ No newline at end of file