diff --git a/serverless-haskell.cabal b/serverless-haskell.cabal
--- a/serverless-haskell.cabal
+++ b/serverless-haskell.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 1e05bad89b0aca82487e2433016e2d39a21f182bf384caab21fb8e56b3a5f2f6
+-- hash: a98a36c8e69422845bcf7370c281cb9b0bbaac07f75b4e660c170bc782ced20c
 
 name:           serverless-haskell
-version:        0.9.4
+version:        0.10.0
 synopsis:       Deploying Haskell code onto AWS Lambda using Serverless
 description:    Utilities to help process the events from AWS Lambda when deployed with the serverless-haskell plugin.
 category:       AWS, Cloud, Network
@@ -30,9 +30,11 @@
       AWSLambda.Events
       AWSLambda.Events.APIGateway
       AWSLambda.Events.KinesisEvent
+      AWSLambda.Events.MessageAttribute
       AWSLambda.Events.Records
       AWSLambda.Events.S3Event
       AWSLambda.Events.SNSEvent
+      AWSLambda.Events.SQSEvent
       AWSLambda.Handler
       AWSLambda.Orphans
       Data.Aeson.Alternative
@@ -72,6 +74,7 @@
       AWSLambda.Events.KinesisEventSpec
       AWSLambda.Events.S3EventSpec
       AWSLambda.Events.SNSEventSpec
+      AWSLambda.Events.SQSEventSpec
       Data.Aeson.AlternativeSpec
       Data.Aeson.EmbeddedSpec
       Data.Aeson.TestUtil
diff --git a/src/AWSLambda/Events.hs b/src/AWSLambda/Events.hs
--- a/src/AWSLambda/Events.hs
+++ b/src/AWSLambda/Events.hs
@@ -11,6 +11,7 @@
 import           AWSLambda.Events.KinesisEvent
 import           AWSLambda.Events.S3Event
 import           AWSLambda.Events.SNSEvent
+import           AWSLambda.Events.SQSEvent
 
 -- | Not yet implemented
 data DynamoDBEvent
@@ -60,11 +61,12 @@
 -- | Sum type for all possible Lambda events.
 -- Parameterised on the type of SNS Events to be handled.
 -- See @SNSEvent@ for details.
-data LambdaEvent snsMessage
+data LambdaEvent message
   = S3 !S3Event
   | DynamoDB !DynamoDBEvent
   | KinesisStream !KinesisEvent
-  | SNS !(SNSEvent snsMessage)
+  | SNS !(SNSEvent message)
+  | SQS !(SQSEvent message)
   | SES !SESEvent
   | Cognito !CognitoEvent
   | CloudFormation !CloudFormationEvent
@@ -84,10 +86,10 @@
 -- | Attempt to parse the various event types.
 -- Any valid JSON that can't be parsed as a specific
 -- event type will result in a 'Custom' value.
-instance FromText snsMessage =>
-         FromJSON (LambdaEvent snsMessage) where
+instance FromText message =>
+         FromJSON (LambdaEvent message) where
   parseJSON v =
-    try S3 v <|> try KinesisStream v <|> try SNS v <|> pure (Custom v)
+    try S3 v <|> try KinesisStream v <|> try SNS v <|> try SQS v <|> pure (Custom v)
     where
       try f = fmap f . parseJSON
 
diff --git a/src/AWSLambda/Events/MessageAttribute.hs b/src/AWSLambda/Events/MessageAttribute.hs
new file mode 100644
--- /dev/null
+++ b/src/AWSLambda/Events/MessageAttribute.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+{-|
+Module: AWSLambda.Events.MessageAttribute
+Description: Types for SQS and SNS message attributes
+-}
+module AWSLambda.Events.MessageAttribute where
+
+import           Control.Lens             (makeLenses)
+import           Data.Aeson.Casing        (aesonPrefix, pascalCase)
+import           Data.Aeson.TH            (deriveFromJSON)
+import           Data.Text                (Text)
+
+data MessageAttribute = MessageAttribute
+  { _maType  :: !Text
+  , _maValue :: !Text
+  } deriving (Eq, Show)
+
+$(deriveFromJSON (aesonPrefix pascalCase) ''MessageAttribute)
+$(makeLenses ''MessageAttribute)
diff --git a/src/AWSLambda/Events/SNSEvent.hs b/src/AWSLambda/Events/SNSEvent.hs
--- a/src/AWSLambda/Events/SNSEvent.hs
+++ b/src/AWSLambda/Events/SNSEvent.hs
@@ -11,12 +11,13 @@
 -}
 module AWSLambda.Events.SNSEvent where
 
+import           Control.Applicative      ((<|>))
 import           Control.Lens
-import           Data.Aeson               (FromJSON (..), genericParseJSON)
+import           Data.Aeson               (FromJSON (..), genericParseJSON,
+                                           withObject, (.:), (.:?), (.!=))
 import           Data.Aeson.Casing        (aesonDrop, pascalCase)
 import           Data.Aeson.Embedded
 import           Data.Aeson.TextValue
-import           Data.Aeson.TH            (deriveFromJSON)
 import           Data.ByteString          (ByteString)
 import           Data.HashMap.Strict      (HashMap)
 import           Data.Text                (Text)
@@ -25,16 +26,9 @@
 import           Network.AWS.Data.Base64
 import           Network.AWS.Data.Text    (FromText)
 
+import           AWSLambda.Events.MessageAttribute
 import           AWSLambda.Events.Records
 
-data MessageAttribute = MessageAttribute
-  { _maType  :: !Text
-  , _maValue :: !Text
-  } deriving (Eq, Show)
-
-$(deriveFromJSON (aesonDrop 3 pascalCase) ''MessageAttribute)
-$(makeLenses ''MessageAttribute)
-
 data SNSMessage message = SNSMessage
   { _smMessage           :: !(TextValue message )
   , _smMessageAttributes :: !(HashMap Text MessageAttribute)
@@ -49,8 +43,28 @@
   , _smUnsubscribeUrl    :: !Text
   } deriving (Eq, Show, Generic)
 
+-- When a lambda is triggered directly off of an SNS topic,
+-- the SNS message contains message attributes and the URI
+-- fields are cased as `SigningCertUrl` and `UnsubscribeUrl`.
+-- When an SNS message is embedded in an SQS event,
+-- the SNS message changes in two ways; `MessageAttributes`
+-- is not present and the casing for the URI fields becomes
+-- `SigningCertURL` and `UnsubscribeURL`.
+-- For these reasons we must hand-roll the `FromJSON` instance.
 instance FromText message => FromJSON (SNSMessage message) where
-  parseJSON = genericParseJSON $ aesonDrop 3 pascalCase
+  parseJSON = withObject "SNSMessage'" $ \o ->
+    SNSMessage
+      <$> o .: "Message"
+      <*> o .:? "MessageAttributes" .!= mempty
+      <*> o .: "MessageId"
+      <*> o .: "Signature"
+      <*> o .: "SignatureVersion"
+      <*> do o .: "SigningCertUrl" <|> o .: "SigningCertURL"
+      <*> o .: "Subject"
+      <*> o .: "Timestamp"
+      <*> o .: "TopicArn"
+      <*> o .: "Type"
+      <*> do o .: "UnsubscribeUrl" <|> o .: "UnsubscribeURL"
 
 $(makeLenses ''SNSMessage)
 
diff --git a/src/AWSLambda/Events/SQSEvent.hs b/src/AWSLambda/Events/SQSEvent.hs
new file mode 100644
--- /dev/null
+++ b/src/AWSLambda/Events/SQSEvent.hs
@@ -0,0 +1,56 @@
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TemplateHaskell   #-}
+
+{-|
+Module: AWSLambda.Events.SQSEvent
+Description: Types for SQS Lambda events
+-}
+module AWSLambda.Events.SQSEvent where
+
+import           Control.Lens
+import           Data.Aeson               (FromJSON (..), genericParseJSON)
+import           Data.Aeson.Casing        (aesonPrefix, camelCase)
+import           Data.Aeson.Embedded
+import           Data.Aeson.TextValue
+import           Data.ByteString          (ByteString)
+import           Data.HashMap.Strict      (HashMap)
+import           Data.Text                (Text)
+import           GHC.Generics             (Generic)
+import           Network.AWS.Data.Base64
+import           Network.AWS.Data.Text    (FromText)
+import qualified Network.AWS.Types        as AWS
+
+import           AWSLambda.Events.MessageAttribute
+import           AWSLambda.Events.Records
+
+data SQSMessage body = SQSMessage
+  { _sqsmMessageId         :: !Text
+  , _sqsmReceiptHandle     :: !Text
+  , _sqsmBody              :: !(TextValue body)
+  , _sqsmAttributes        :: !(HashMap Text Text)
+  , _sqsmMessageAttributes :: !(HashMap Text MessageAttribute)
+  , _sqsmMd5OfBody         :: !Text
+  , _sqsmEventSource       :: !Text
+  , _sqsmEventSourceARN    :: !Text
+  , _sqsmAwsRegion         :: !AWS.Region
+  } deriving (Show, Eq, Generic)
+
+instance FromText message => FromJSON (SQSMessage message) where
+  parseJSON = genericParseJSON $ aesonPrefix camelCase
+
+$(makeLenses ''SQSMessage)
+
+type SQSEvent body = RecordsEvent (SQSMessage body)
+
+-- | A Traversal to get messages from an SQSEvent
+sqsMessages :: Traversal (SQSEvent message) (SQSEvent message') message message'
+sqsMessages = reRecords . traverse . sqsmBody . unTextValue
+
+-- | A Traversal to get embedded JSON values from an SQSEvent
+sqsEmbedded :: Traversal (SQSEvent (Embedded v)) (SQSEvent (Embedded v')) v v'
+sqsEmbedded = sqsMessages . unEmbed
+
+sqsBinary :: Traversal' (SQSEvent Base64) ByteString
+sqsBinary = sqsMessages . _Base64
diff --git a/test/AWSLambda/Events/SNSEventSpec.hs b/test/AWSLambda/Events/SNSEventSpec.hs
--- a/test/AWSLambda/Events/SNSEventSpec.hs
+++ b/test/AWSLambda/Events/SNSEventSpec.hs
@@ -3,6 +3,7 @@
 
 module AWSLambda.Events.SNSEventSpec where
 
+import           AWSLambda.Events.MessageAttribute
 import           AWSLambda.Events.Records
 import           AWSLambda.Events.S3Event
 import           AWSLambda.Events.SNSEvent
diff --git a/test/AWSLambda/Events/SQSEventSpec.hs b/test/AWSLambda/Events/SQSEventSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/AWSLambda/Events/SQSEventSpec.hs
@@ -0,0 +1,201 @@
+{-# LANGUAGE OverloadedLists #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+
+module AWSLambda.Events.SQSEventSpec where
+
+import           AWSLambda.Events.MessageAttribute
+import           AWSLambda.Events.Records
+import           AWSLambda.Events.S3Event
+import           AWSLambda.Events.SNSEvent
+import           AWSLambda.Events.SQSEvent
+
+import           Data.Aeson
+import           Data.Aeson.Embedded
+import           Data.Aeson.TextValue
+import           Data.ByteString.Lazy      (ByteString)
+import           Data.Text                 (Text)
+import           Data.Time.Calendar
+import           Data.Time.Clock
+import           Network.AWS.S3            as S3
+
+import           Text.RawString.QQ
+
+import           Test.Hspec
+
+spec :: Spec
+spec = describe "Handler" $ do
+  it "parses sample text event" $
+    decode sampleSQSJSON `shouldBe` Just sampleSQSEvent
+  it "parses sample embedded S3 -> SNS -> SQS event" $
+    eitherDecode sampleS3SNSSQSJSON `shouldBe` Right sampleS3SNSSQSEvent
+
+sampleSQSJSON :: ByteString
+sampleSQSJSON = [r|
+{
+  "Records": [
+    {
+      "messageId": "b792b6ba-b444-48c5-9cd8-29b8ad373eae",
+      "receiptHandle": "ReceiptHandle",
+      "body": "Hello from SQS!",
+      "attributes": {
+        "ApproximateReceiveCount": "1",
+        "SentTimestamp": "1572575717896",
+        "SenderId": "AIDAIY4XCTD3OFZN5ED42",
+        "ApproximateFirstReceiveTimestamp": "1572575717898"
+      },
+      "messageAttributes": {
+        "Test": {
+          "Type": "String",
+          "Value": "TestString"
+        },
+        "TestBinary": {
+          "Type": "Binary",
+          "Value": "TestBinary"
+        }
+      },
+      "md5OfBody": "aca137746cb7d6a8e3eda3d1ce09b0c5",
+      "eventSource": "aws:sqs",
+      "eventSourceARN": "arn:aws:sqs:ap-southeast-2:1234567890:my-queue",
+      "awsRegion": "ap-southeast-2"
+    }
+  ]
+}
+|]
+
+sampleSQSEvent :: SQSEvent Text
+sampleSQSEvent =
+  RecordsEvent
+    [ SQSMessage
+        { _sqsmMessageId         = "b792b6ba-b444-48c5-9cd8-29b8ad373eae"
+        , _sqsmReceiptHandle     = "ReceiptHandle"
+        , _sqsmBody              = "Hello from SQS!"
+        , _sqsmAttributes        =
+          [ ("ApproximateReceiveCount", "1")
+          , ("SentTimestamp", "1572575717896")
+          , ("SenderId", "AIDAIY4XCTD3OFZN5ED42")
+          , ("ApproximateFirstReceiveTimestamp", "1572575717898")
+          ]
+        , _sqsmMessageAttributes =
+          [ ( "Test"
+            , MessageAttribute
+              { _maType = "String"
+              , _maValue = "TestString"
+              })
+          , ( "TestBinary"
+            , MessageAttribute
+              { _maType = "Binary"
+              , _maValue = "TestBinary"
+              })
+          ]
+        , _sqsmMd5OfBody         = "aca137746cb7d6a8e3eda3d1ce09b0c5"
+        , _sqsmEventSource       = "aws:sqs"
+        , _sqsmEventSourceARN    = "arn:aws:sqs:ap-southeast-2:1234567890:my-queue"
+        , _sqsmAwsRegion         = Sydney
+        }
+      ]
+
+sampleS3SNSSQSJSON :: ByteString
+sampleS3SNSSQSJSON = [r|
+{
+  "Records": [
+    {
+      "messageId": "b792b6ba-b444-48c5-9cd8-29b8ad373eae",
+      "receiptHandle": "ReceiptHandle",
+      "body": "{\n  \"Type\" : \"Notification\",\n  \"MessageId\" : \"MessageId\",\n  \"TopicArn\" : \"arn:aws:sns:ap-southeast-2:11111111111111:my-topic\",\n  \"Subject\" : \"Amazon S3 Notification\",\n  \"Message\" : \"{\\\"Records\\\":[{\\\"eventVersion\\\":\\\"2.1\\\",\\\"eventSource\\\":\\\"aws:s3\\\",\\\"awsRegion\\\":\\\"ap-southeast-2\\\",\\\"eventTime\\\":\\\"2019-11-01T00:00:00.00Z\\\",\\\"eventName\\\":\\\"ObjectCreated:Put\\\",\\\"userIdentity\\\":{\\\"principalId\\\":\\\"AWS:AHJD568HF4356HJJ:bob\\\"},\\\"requestParameters\\\":{\\\"sourceIPAddress\\\":\\\"787.39.11.220\\\"},\\\"responseElements\\\":{\\\"x-amz-request-id\\\":\\\"GDJS6765sJSHSS\\\",\\\"x-amz-id-2\\\":\\\"ID2\\\"},\\\"s3\\\":{\\\"s3SchemaVersion\\\":\\\"1.0\\\",\\\"configurationId\\\":\\\"ConfigurationId\\\",\\\"bucket\\\":{\\\"name\\\":\\\"my-bucket\\\",\\\"ownerIdentity\\\":{\\\"principalId\\\":\\\"ASKD794UDYDH\\\"},\\\"arn\\\":\\\"arn:aws:s3:::my-bucket\\\"},\\\"object\\\":{\\\"key\\\":\\\"my-key\\\",\\\"size\\\":13315,\\\"eTag\\\":\\\"1231234fabf233124124\\\",\\\"versionId\\\":\\\"735hjf893ufb8fhuf\\\",\\\"sequencer\\\":\\\"HUJKFDHJD8656567HGGSGJKD\\\"}}}]}\",\n  \"Timestamp\" : \"2019-11-01T00:00:00Z\",\n  \"SignatureVersion\" : \"1\",\n  \"Signature\" : \"Signature\",\n  \"SigningCertURL\" : \"https://sns.ap-southeast-2.amazonaws.com/SimpleNotificationService-my-cert.pem\",\n  \"UnsubscribeURL\" : \"https://sns.ap-southeast-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-southeast-2:11111111111111:my-topic:unsub\"\n}",
+      "attributes": {
+        "ApproximateReceiveCount": "1",
+        "SentTimestamp": "1572575717896",
+        "SenderId": "AIDAIY4XCTD3OFZN5ED42",
+        "ApproximateFirstReceiveTimestamp": "1572575717898"
+      },
+      "messageAttributes": {
+        "Test": {
+          "Type": "String",
+          "Value": "TestString"
+        },
+        "TestBinary": {
+          "Type": "Binary",
+          "Value": "TestBinary"
+        }
+      },
+      "md5OfBody": "aca137746cb7d6a8e3eda3d1ce09b0c5",
+      "eventSource": "aws:sqs",
+      "eventSourceARN": "arn:aws:sqs:ap-southeast-2:1234567890:my-queue",
+      "awsRegion": "ap-southeast-2"
+    }
+  ]
+}
+|]
+
+sampleS3SNSSQSEvent :: SQSEvent (Embedded (SNSMessage (Embedded S3Event)))
+sampleS3SNSSQSEvent =
+  RecordsEvent
+    [ SQSMessage
+        { _sqsmMessageId         = "b792b6ba-b444-48c5-9cd8-29b8ad373eae"
+        , _sqsmReceiptHandle     = "ReceiptHandle"
+        , _sqsmBody              =
+          TextValue $ Embedded $ SNSMessage
+            { _smMessage                = TextValue $ Embedded $ RecordsEvent
+              [ S3EventNotification
+                { _senAwsRegion         = Sydney
+                , _senEventName         = S3ObjectCreatedPut
+                , _senEventSource       = "aws:s3"
+                , _senEventTime         = UTCTime (fromGregorian 2019 11 1) 0
+                , _senEventVersion      = "2.1"
+                , _senRequestParameters = RequestParametersEntity "787.39.11.220"
+                , _senResponseElements  = ResponseElementsEntity
+                  { _reeXAmzId2         = "ID2"
+                  , _reeXAmzRequestId   = "GDJS6765sJSHSS" }
+                , _senS3                = S3Entity
+                  { _seBucket           = S3BucketEntity
+                    { _sbeArn           = "arn:aws:s3:::my-bucket"
+                    , _sbeName          = BucketName "my-bucket"
+                    , _sbeOwnerIdentity = UserIdentityEntity "ASKD794UDYDH" }
+                  , _seConfigurationId  = "ConfigurationId"
+                  , _seObject           = S3ObjectEntity
+                    { _soeETag          = Just (ETag "1231234fabf233124124")
+                    , _soeKey           = ObjectKey "my-key"
+                    , _soeSize          = Just 13315
+                    , _soeSequencer     = "HUJKFDHJD8656567HGGSGJKD"
+                    , _soeVersionId     = Just "735hjf893ufb8fhuf" }
+                  , _seS3SchemaVersion = "1.0"
+                  }
+                , _senUserIdentity = UserIdentityEntity "AWS:AHJD568HF4356HJJ:bob"
+                }
+              ]
+            , _smMessageAttributes = mempty
+            , _smMessageId         = "MessageId"
+            , _smSignature         = "Signature"
+            , _smSignatureVersion  = "1"
+            , _smSigningCertUrl    = "https://sns.ap-southeast-2.amazonaws.com/SimpleNotificationService-my-cert.pem"
+            , _smSubject           = "Amazon S3 Notification"
+            , _smTimestamp         = UTCTime (fromGregorian 2019 11 1) 0
+            , _smTopicArn          = "arn:aws:sns:ap-southeast-2:11111111111111:my-topic"
+            , _smType              = "Notification"
+            , _smUnsubscribeUrl    = "https://sns.ap-southeast-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-southeast-2:11111111111111:my-topic:unsub"
+          }
+        , _sqsmAttributes        =
+          [ ("ApproximateReceiveCount", "1")
+          , ("SentTimestamp", "1572575717896")
+          , ("SenderId", "AIDAIY4XCTD3OFZN5ED42")
+          , ("ApproximateFirstReceiveTimestamp", "1572575717898")
+          ]
+        , _sqsmMessageAttributes =
+          [ ( "Test"
+            , MessageAttribute
+              { _maType = "String"
+              , _maValue = "TestString"
+              })
+          , ( "TestBinary"
+            , MessageAttribute
+              { _maType = "Binary"
+              , _maValue = "TestBinary"
+              })
+          ]
+        , _sqsmMd5OfBody         = "aca137746cb7d6a8e3eda3d1ce09b0c5"
+        , _sqsmEventSource       = "aws:sqs"
+        , _sqsmEventSourceARN    = "arn:aws:sqs:ap-southeast-2:1234567890:my-queue"
+        , _sqsmAwsRegion         = Sydney
+        }
+      ]
