diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/aws-dynamodb-streams.cabal b/aws-dynamodb-streams.cabal
new file mode 100644
--- /dev/null
+++ b/aws-dynamodb-streams.cabal
@@ -0,0 +1,50 @@
+name:                aws-dynamodb-streams
+version:             0.1.0.0
+synopsis:            Haskell bindings for Amazon DynamoDB Streams
+-- description:
+license:             Apache-2.0
+license-file:        LICENSE
+author:              Jon Sterling & Lars Kuhtz
+maintainer:          jon@jonmsterling.com
+copyright:           Copyright (c) 2013-2014 PivotCloud, Inc.
+category:            Network
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Aws.DynamoDb.Streams,
+                       Aws.DynamoDb.Streams.Core,
+                       Aws.DynamoDb.Streams.Types
+                       Aws.DynamoDb.Streams.Commands.ListStreams
+                       Aws.DynamoDb.Streams.Commands.DescribeStream
+                       Aws.DynamoDb.Streams.Commands.GetShardIterator
+                       Aws.DynamoDb.Streams.Commands.GetRecords
+  build-depends:       base >=4.7 && <4.8,
+                       base-unicode-symbols,
+                       attoparsec >=0.12.0.0,
+                       aeson >=0.8.0.0,
+                       aws >=0.10.5,
+                       aws-general >=0.1,
+                       base16-bytestring >=0.1.1.6,
+                       base64-bytestring >=1.0.0.0,
+                       byteable >=0.1.1,
+                       bytestring >=0.10.4.0,
+                       cryptohash >=0.11.6,
+                       containers >=0.5.5.0,
+                       case-insensitive >=1.2.0.0,
+                       conduit >=1.2.0,
+                       conduit-extra >=1.1.4,
+                       http-types >=0.0.8.5,
+                       http-conduit >=2.1.5,
+                       old-locale >=1.0.0.0,
+                       mtl >=2.1,
+                       parsers >=0.11,
+                       profunctors >=4.2,
+                       resourcet >=1.1.3,
+                       scientific >=0.3.3.2,
+                       text >=1.2.0.0,
+                       time >=1.4.2
+  hs-source-dirs:      src
+  ghc-options:         -Wall
+  default-language:    Haskell2010
diff --git a/src/Aws/DynamoDb/Streams.hs b/src/Aws/DynamoDb/Streams.hs
new file mode 100644
--- /dev/null
+++ b/src/Aws/DynamoDb/Streams.hs
@@ -0,0 +1,37 @@
+-- Copyright (c) 2013-2014 PivotCloud, Inc.
+--
+-- Aws.DynamoDb.Streams
+--
+-- Please feel free to contact us at licensing@pivotmail.com with any
+-- contributions, additions, or other feedback; we would love to hear from
+-- you.
+--
+-- Licensed 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.
+
+-- |
+-- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc.
+-- License: Apache-2.0
+--
+module Aws.DynamoDb.Streams
+( module Aws.DynamoDb.Streams.Core
+, module Aws.DynamoDb.Streams.Types
+, module Aws.DynamoDb.Streams.Commands.ListStreams
+, module Aws.DynamoDb.Streams.Commands.DescribeStream
+, module Aws.DynamoDb.Streams.Commands.GetShardIterator
+, module Aws.DynamoDb.Streams.Commands.GetRecords
+) where
+
+import Aws.DynamoDb.Streams.Core
+import Aws.DynamoDb.Streams.Types
+import Aws.DynamoDb.Streams.Commands.ListStreams
+import Aws.DynamoDb.Streams.Commands.DescribeStream
+import Aws.DynamoDb.Streams.Commands.GetShardIterator
+import Aws.DynamoDb.Streams.Commands.GetRecords
diff --git a/src/Aws/DynamoDb/Streams/Commands/DescribeStream.hs b/src/Aws/DynamoDb/Streams/Commands/DescribeStream.hs
new file mode 100644
--- /dev/null
+++ b/src/Aws/DynamoDb/Streams/Commands/DescribeStream.hs
@@ -0,0 +1,209 @@
+-- Copyright (c) 2013-2014 PivotCloud, Inc.
+--
+-- Aws.DynamoDb.Streams.Commands.DescribeStream
+--
+-- Please feel free to contact us at licensing@pivotmail.com with any
+-- contributions, additions, or other feedback; we would love to hear from
+-- you.
+--
+-- Licensed 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.
+
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UnicodeSyntax #-}
+
+-- |
+-- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc.
+-- License: Apache-2.0
+--
+module Aws.DynamoDb.Streams.Commands.DescribeStream
+( -- * Request
+  DescribeStream(..)
+, describeStream
+  -- ** Lenses
+, dstExclusiveStartShardId
+, dstLimit
+, dstStreamId
+
+  -- * Response
+, DescribeStreamResponse(..)
+  -- ** Lenses
+, dstrStreamDescription
+) where
+
+import Aws.Core
+import Aws.DynamoDb.Streams.Core
+import Aws.DynamoDb.Streams.Types
+import Control.Applicative
+import Control.Applicative.Unicode
+import Data.Aeson
+import Data.Typeable
+import Prelude.Unicode
+
+data DescribeStream
+  = DescribeStream
+  { _dstExclusiveStartShardId ∷ !(Maybe ShardId)
+    -- ^ The shard id of the first item this operation will evalute; see also
+    -- '_sdLastEvaluatedShardId'.
+
+  , _dstLimit ∷ !(Maybe Int)
+    -- ^ The maximum number of shard objects to return.
+
+  , _dstStreamId ∷ !StreamId
+    -- ^ The unique ID of the stream to be described.
+
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+-- | A basic 'DescribeStream' request for a given stream id.
+--
+-- @
+-- myRequest =
+--   'describeStream' myStream
+--     & 'dstLimit' ?~ 3
+-- @
+--
+describeStream
+  ∷ StreamId
+  → DescribeStream
+describeStream streamId = DescribeStream
+  { _dstExclusiveStartShardId = Nothing
+  , _dstLimit = Nothing
+  , _dstStreamId = streamId
+  }
+
+instance ToJSON DescribeStream where
+  toJSON DescribeStream{..} = object
+    [ "ExclusiveStartShardId" .= _dstExclusiveStartShardId
+    , "Limit" .= _dstLimit
+    , "StreamId".= _dstStreamId
+    ]
+
+instance FromJSON DescribeStream where
+  parseJSON =
+    withObject "DescribeStream" $ \o →
+      pure DescribeStream
+        ⊛ o .:? "ExclusiveStartShardId"
+        ⊛ o .:? "Limit"
+        ⊛ o .: "StreamId"
+
+-- | A lens for '_dstExclusiveStartShardId'.
+--
+-- @
+-- 'dstExclusiveStartShardId' ∷ Lens' 'DescribeStream' ('Maybe' 'ShardId')
+-- @
+--
+dstExclusiveStartShardId
+  ∷ Functor f
+  ⇒ (Maybe ShardId → f (Maybe ShardId))
+  → DescribeStream
+  → f DescribeStream
+dstExclusiveStartShardId i DescribeStream{..} =
+  (\_dstExclusiveStartShardId → DescribeStream{..})
+    <$> i _dstExclusiveStartShardId
+{-# INLINE dstExclusiveStartShardId #-}
+
+-- | A lens for '_dstLimit'.
+--
+-- @
+-- 'dstLimit' ∷ Lens' 'DescribeStream' ('Maybe' 'Int')
+-- @
+--
+dstLimit
+  ∷ Functor f
+  ⇒ (Maybe Int → f (Maybe Int))
+  → DescribeStream
+  → f DescribeStream
+dstLimit i DescribeStream{..} =
+  (\_dstLimit → DescribeStream{..})
+    <$> i _dstLimit
+{-# INLINE dstLimit #-}
+
+-- | A lens for '_dstStreamId'.
+--
+-- @
+-- 'dstStreamId' ∷ Lens' 'DescribeStream' 'StreamId'
+-- @
+--
+dstStreamId
+  ∷ Functor f
+  ⇒ (StreamId → f StreamId)
+  → DescribeStream
+  → f DescribeStream
+dstStreamId i DescribeStream{..} =
+  (\_dstStreamId → DescribeStream{..})
+    <$> i _dstStreamId
+{-# INLINE dstStreamId #-}
+
+data DescribeStreamResponse
+  = DescribeStreamResponse
+  { _dstrStreamDescription ∷ !StreamDescription
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON DescribeStreamResponse where
+  toJSON DescribeStreamResponse{..} = object
+    [ "StreamDescription" .= _dstrStreamDescription
+    ]
+
+instance FromJSON DescribeStreamResponse where
+  parseJSON =
+    withObject "DescribeStreamResponse" $ \o →
+      DescribeStreamResponse
+        <$> o .: "StreamDescription"
+
+
+-- | A lens for '_dstrStreamDescription'.
+--
+-- @
+-- 'dstrStreamDescription' ∷ Lens' 'DescribeStreamResponse' 'StreamDescription'
+-- @
+--
+dstrStreamDescription
+  ∷ Functor f
+  ⇒ (StreamDescription → f StreamDescription)
+  → DescribeStreamResponse
+  → f DescribeStreamResponse
+dstrStreamDescription i DescribeStreamResponse{..} =
+  (\_dstrStreamDescription → DescribeStreamResponse{..})
+    <$> i _dstrStreamDescription
+{-# INLINE dstrStreamDescription #-}
+
+instance ResponseConsumer r DescribeStreamResponse where
+  type ResponseMetadata DescribeStreamResponse = StreamsMetadata
+  responseConsumer _ = streamsResponseConsumer
+
+instance SignQuery DescribeStream where
+  type ServiceConfiguration DescribeStream = StreamsConfiguration
+  signQuery cmd = streamsSignQuery StreamsQuery
+    { _stqAction = ActionDescribeStream
+    , _stqBody = encode cmd
+    }
+
+instance Transaction DescribeStream DescribeStreamResponse
+
+instance AsMemoryResponse DescribeStreamResponse where
+  type MemoryResponse DescribeStreamResponse = DescribeStreamResponse
+  loadToMemory = return
+
+instance ListResponse DescribeStreamResponse Shard where
+  listResponse = _sdShards ∘ _dstrStreamDescription
+
+instance IteratedTransaction DescribeStream DescribeStreamResponse where
+  nextIteratedRequest req@DescribeStream{..} DescribeStreamResponse{..} = do
+    let StreamDescription{..} = _dstrStreamDescription
+    lastEvaluatedShardId ← _sdLastEvaluatedShardId
+    return req
+      { _dstExclusiveStartShardId = Just lastEvaluatedShardId
+      }
+
diff --git a/src/Aws/DynamoDb/Streams/Commands/GetRecords.hs b/src/Aws/DynamoDb/Streams/Commands/GetRecords.hs
new file mode 100644
--- /dev/null
+++ b/src/Aws/DynamoDb/Streams/Commands/GetRecords.hs
@@ -0,0 +1,195 @@
+-- Copyright (c) 2013-2014 PivotCloud, Inc.
+--
+-- Aws.DynamoDb.Streams.Commands.GetRecords
+--
+-- Please feel free to contact us at licensing@pivotmail.com with any
+-- contributions, additions, or other feedback; we would love to hear from
+-- you.
+--
+-- Licensed 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.
+
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UnicodeSyntax #-}
+
+-- |
+-- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc.
+-- License: Apache-2.0
+--
+module Aws.DynamoDb.Streams.Commands.GetRecords
+( -- * Request
+  GetRecords(..)
+, getRecords
+  -- ** Lenses
+, grShardIterator
+, grLimit
+
+  -- * Response
+, GetRecordsResponse(..)
+  -- ** Lenses
+, grrRecords
+, grrNextShardIterator
+) where
+
+import Aws.Core
+import Aws.DynamoDb.Streams.Core
+import Aws.DynamoDb.Streams.Types
+import Control.Applicative
+import Control.Applicative.Unicode
+import Data.Aeson
+import Data.Typeable
+
+data GetRecords
+  = GetRecords
+  { _grShardIterator ∷ !ShardIterator
+  , _grLimit ∷ !(Maybe Int)
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+-- | A basic 'GetRecords' request for a given shard iterator.
+--
+-- @
+-- myRequest = getRecords it & grLimit ?~ 42
+-- @
+--
+getRecords
+  ∷ ShardIterator
+  → GetRecords
+getRecords it = GetRecords
+  { _grShardIterator = it
+  , _grLimit = Nothing
+  }
+
+instance ToJSON GetRecords where
+  toJSON GetRecords{..} = object
+    [ "ShardIterator" .= _grShardIterator
+    , "Limit" .= _grLimit
+    ]
+
+instance FromJSON GetRecords where
+  parseJSON =
+    withObject "GetRecords" $ \o →
+      pure GetRecords
+        ⊛ o .: "ShardIterator"
+        ⊛ o .:? "Limit"
+
+-- | A lens for '_grShardIterator'.
+--
+-- @
+-- grShardIterator ∷ Lens' 'GetRecords' 'ShardIterator'
+-- @
+--
+grShardIterator
+  ∷ Functor f
+  ⇒ (ShardIterator → f ShardIterator)
+  → GetRecords
+  → f GetRecords
+grShardIterator i GetRecords{..} =
+  (\_grShardIterator → GetRecords{..})
+    <$> i _grShardIterator
+{-# INLINE grShardIterator #-}
+
+-- | A lens for '_grLimit'.
+--
+-- @
+-- grLimit ∷ Lens' 'GetRecords' ('Maybe' 'Int')
+-- @
+--
+grLimit
+  ∷ Functor f
+  ⇒ (Maybe Int → f (Maybe Int))
+  → GetRecords
+  → f GetRecords
+grLimit i GetRecords{..} =
+  (\_grLimit → GetRecords{..})
+    <$> i _grLimit
+{-# INLINE grLimit #-}
+
+data GetRecordsResponse
+  = GetRecordsResponse
+  { _grrNextShardIterator ∷ !(Maybe ShardIterator)
+  , _grrRecords ∷ ![Record]
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON GetRecordsResponse where
+  toJSON GetRecordsResponse{..} = object
+    [ "NextShardIterator" .= _grrNextShardIterator
+    , "Records" .= _grrRecords
+    ]
+
+instance FromJSON GetRecordsResponse where
+  parseJSON =
+    withObject "GetRecordsResponse" $ \o →
+      pure GetRecordsResponse
+        ⊛ o .:? "NextShardIterator"
+        ⊛ o .: "Records"
+
+-- | A lens for '_grrRecords'.
+--
+-- @
+-- grrRecords ∷ Lens' 'GetRecordsResponse' ['Record']
+-- @
+--
+grrRecords
+  ∷ Functor f
+  ⇒ ([Record] → f [Record])
+  → GetRecordsResponse
+  → f GetRecordsResponse
+grrRecords i GetRecordsResponse{..} =
+  (\_grrRecords → GetRecordsResponse{..})
+    <$> i _grrRecords
+{-# INLINE grrRecords #-}
+
+-- | A lens for '_grrNextShardIterator'.
+--
+-- @
+-- grrNextShardIterator ∷ Lens' 'GetRecordsResponse' ('Maybe' 'ShardIterator')
+-- @
+--
+grrNextShardIterator
+  ∷ Functor f
+  ⇒ (Maybe ShardIterator → f (Maybe ShardIterator))
+  → GetRecordsResponse
+  → f GetRecordsResponse
+grrNextShardIterator i GetRecordsResponse{..} =
+  (\_grrNextShardIterator → GetRecordsResponse{..})
+    <$> i _grrNextShardIterator
+{-# INLINE grrNextShardIterator #-}
+
+instance ResponseConsumer r GetRecordsResponse where
+  type ResponseMetadata GetRecordsResponse = StreamsMetadata
+  responseConsumer _ = streamsResponseConsumer
+
+instance SignQuery GetRecords where
+  type ServiceConfiguration GetRecords = StreamsConfiguration
+  signQuery cmd = streamsSignQuery StreamsQuery
+    { _stqAction = ActionGetRecords
+    , _stqBody = encode cmd
+    }
+
+instance Transaction GetRecords GetRecordsResponse
+
+instance AsMemoryResponse GetRecordsResponse where
+  type MemoryResponse GetRecordsResponse = GetRecordsResponse
+  loadToMemory = return
+
+instance ListResponse GetRecordsResponse Record where
+  listResponse = _grrRecords
+
+instance IteratedTransaction GetRecords GetRecordsResponse where
+  nextIteratedRequest req@GetRecords{..} GetRecordsResponse{..} = do
+    nextShardIterator ← _grrNextShardIterator
+    return req
+      { _grShardIterator = nextShardIterator
+      }
diff --git a/src/Aws/DynamoDb/Streams/Commands/GetShardIterator.hs b/src/Aws/DynamoDb/Streams/Commands/GetShardIterator.hs
new file mode 100644
--- /dev/null
+++ b/src/Aws/DynamoDb/Streams/Commands/GetShardIterator.hs
@@ -0,0 +1,225 @@
+-- Copyright (c) 2013-2014 PivotCloud, Inc.
+--
+-- Aws.DynamoDb.Streams.Commands.GetShardIterator
+--
+-- Please feel free to contact us at licensing@pivotmail.com with any
+-- contributions, additions, or other feedback; we would love to hear from
+-- you.
+--
+-- Licensed 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.
+
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UnicodeSyntax #-}
+
+-- |
+-- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc.
+-- License: Apache-2.0
+--
+module Aws.DynamoDb.Streams.Commands.GetShardIterator
+( -- * Request
+  GetShardIterator(..)
+, getShardIterator
+  -- ** Lenses
+, gsiSequenceNumber
+, gsiShardId
+, gsiShardIteratorType
+, gsiStreamId
+
+  -- * Response
+, GetShardIteratorResponse(..)
+  -- ** Lenses
+, gsirShardIterator
+) where
+
+import Aws.Core
+import Aws.DynamoDb.Streams.Core
+import Aws.DynamoDb.Streams.Types
+import Control.Applicative
+import Control.Applicative.Unicode
+import Data.Aeson
+import Data.Typeable
+
+-- | A shard iterator provides information about how to retrieve stream records
+-- from within a shard. Note: A shard iterator expires 15 minutes after it is
+-- returned to the requester.
+--
+data GetShardIterator
+  = GetShardIterator
+  { _gsiSequenceNumber ∷ !(Maybe SequenceNumber)
+    -- ^ The sequence number of a stream record in the shard from which to
+    -- start reading.
+
+  , _gsiShardId ∷ !ShardId
+    -- ^ The identifier of the shard.
+
+  , _gsiShardIteratorType ∷ !ShardIteratorType
+    -- ^ Determines how the shard iterator is used to start reading stream
+    -- records from the shard.
+
+  , _gsiStreamId ∷ !StreamId
+  } deriving (Eq, Ord, Read, Show, Typeable)
+
+-- | A basic 'GetShardIterator' request for a given stream id.
+--
+-- @
+-- myRequest =
+--   'getShardIterator' myStream myShard 'ShardIteratorLatest'
+--     & 'gsiSequenceNumber' ?~ sqn
+-- @
+--
+getShardIterator
+  ∷ StreamId
+  → ShardId
+  → ShardIteratorType
+  → GetShardIterator
+getShardIterator stream shard iteratorType =
+  GetShardIterator
+    { _gsiStreamId = stream
+    , _gsiShardIteratorType = iteratorType
+    , _gsiShardId = shard
+    , _gsiSequenceNumber = Nothing
+    }
+
+instance ToJSON GetShardIterator where
+  toJSON GetShardIterator{..} = object
+    [ "SequenceNumber" .= _gsiSequenceNumber
+    , "ShardId" .= _gsiShardId
+    , "ShardIteratorType" .= _gsiShardIteratorType
+    , "StreamId" .= _gsiStreamId
+    ]
+
+instance FromJSON GetShardIterator where
+  parseJSON =
+    withObject "GetShardIterator" $ \o →
+      pure GetShardIterator
+        ⊛ o .:? "SequenceNumber"
+        ⊛ o .: "ShardId"
+        ⊛ o .: "ShardIteratorType"
+        ⊛ o .: "StreamId"
+
+-- | A lens for '_gsiSequenceNumber'.
+--
+-- @
+-- 'gsiSequenceNumber' ∷ Lens' 'GetShardIterator' ('Maybe' 'SequenceNumber')
+-- @
+--
+gsiSequenceNumber
+  ∷ Functor f
+  ⇒ (Maybe SequenceNumber → f (Maybe SequenceNumber))
+  → GetShardIterator
+  → f GetShardIterator
+gsiSequenceNumber i GetShardIterator{..} =
+  (\_gsiSequenceNumber → GetShardIterator{..})
+    <$> i _gsiSequenceNumber
+{-# INLINE gsiSequenceNumber #-}
+
+-- | A lens for '_gsiShardId'.
+--
+-- @
+-- 'gsiShardId' ∷ Lens' 'GetShardIterator' 'ShardId'
+-- @
+--
+gsiShardId
+  ∷ Functor f
+  ⇒ (ShardId → f ShardId)
+  → GetShardIterator
+  → f GetShardIterator
+gsiShardId i GetShardIterator{..} =
+  (\_gsiShardId → GetShardIterator{..})
+    <$> i _gsiShardId
+{-# INLINE gsiShardId #-}
+
+-- | A lens for '_gsiShardIteratorType'.
+--
+-- @
+-- 'gsiShardIteratorType' ∷ Lens' 'GetShardIterator' 'ShardIteratorType'
+-- @
+--
+gsiShardIteratorType
+  ∷ Functor f
+  ⇒ (ShardIteratorType → f ShardIteratorType)
+  → GetShardIterator
+  → f GetShardIterator
+gsiShardIteratorType i GetShardIterator{..} =
+  (\_gsiShardIteratorType → GetShardIterator{..})
+    <$> i _gsiShardIteratorType
+{-# INLINE gsiShardIteratorType #-}
+
+-- | A lens for '_gsiStreamId'.
+--
+-- @
+-- 'gsiStreamId' ∷ Lens' 'GetShardIterator' 'StreamId'
+-- @
+--
+gsiStreamId
+  ∷ Functor f
+  ⇒ (StreamId → f StreamId)
+  → GetShardIterator
+  → f GetShardIterator
+gsiStreamId i GetShardIterator{..} =
+  (\_gsiStreamId → GetShardIterator{..})
+    <$> i _gsiStreamId
+{-# INLINE gsiStreamId #-}
+
+data GetShardIteratorResponse
+  = GetShardIteratorResponse
+  { _gsirShardIterator ∷ !ShardIterator
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON GetShardIteratorResponse where
+  toJSON GetShardIteratorResponse{..} = object
+    [ "ShardIterator" .= _gsirShardIterator
+    ]
+
+instance FromJSON GetShardIteratorResponse where
+  parseJSON =
+    withObject "GetShardIteratorResponse" $ \o →
+      GetShardIteratorResponse
+        <$> o .: "ShardIterator"
+
+-- | A lens for '_gsirShardIterator'.
+--
+-- @
+-- 'gsirShardIterator' ∷ Lens' 'GetShardIteratorResponse' 'ShardIterator'
+-- @
+--
+gsirShardIterator
+  ∷ Functor f
+  ⇒ (ShardIterator → f ShardIterator)
+  → GetShardIteratorResponse
+  → f GetShardIteratorResponse
+gsirShardIterator i GetShardIteratorResponse{..} =
+  (\_gsirShardIterator → GetShardIteratorResponse{..})
+    <$> i _gsirShardIterator
+{-# INLINE gsirShardIterator #-}
+
+instance ResponseConsumer r GetShardIteratorResponse where
+  type ResponseMetadata GetShardIteratorResponse = StreamsMetadata
+  responseConsumer _ = streamsResponseConsumer
+
+instance SignQuery GetShardIterator where
+  type ServiceConfiguration GetShardIterator = StreamsConfiguration
+  signQuery cmd = streamsSignQuery StreamsQuery
+    { _stqAction = ActionGetShardIterator
+    , _stqBody = encode cmd
+    }
+
+instance Transaction GetShardIterator GetShardIteratorResponse
+
+instance AsMemoryResponse GetShardIteratorResponse where
+  type MemoryResponse GetShardIteratorResponse = GetShardIteratorResponse
+  loadToMemory = return
+
diff --git a/src/Aws/DynamoDb/Streams/Commands/ListStreams.hs b/src/Aws/DynamoDb/Streams/Commands/ListStreams.hs
new file mode 100644
--- /dev/null
+++ b/src/Aws/DynamoDb/Streams/Commands/ListStreams.hs
@@ -0,0 +1,242 @@
+-- Copyright (c) 2013-2014 PivotCloud, Inc.
+--
+-- Aws.DynamoDb.Streams.Commands.ListStreams
+--
+-- Please feel free to contact us at licensing@pivotmail.com with any
+-- contributions, additions, or other feedback; we would love to hear from
+-- you.
+--
+-- Licensed 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.
+
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UnicodeSyntax #-}
+
+-- |
+-- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc.
+-- License: Apache-2.0
+--
+module Aws.DynamoDb.Streams.Commands.ListStreams
+( -- * Request
+  ListStreams(..)
+, listStreams
+  -- ** Lenses
+, lstExclusiveStartStreamId
+, lstLimit
+, lstTableName
+
+  -- * Response
+, ListStreamsResponse(..)
+
+  -- ** Lenses
+, lstrLastEvalutedStreamId
+, lstrStreamIds
+) where
+
+import Aws.Core
+import Aws.DynamoDb.Streams.Core
+import Aws.DynamoDb.Streams.Types
+
+import Control.Applicative
+import Control.Applicative.Unicode
+import Data.Aeson
+import Data.Monoid
+import Data.Typeable
+
+import qualified Data.Text as T
+
+data ListStreams
+  = ListStreams
+  { _lstExclusiveStartStreamId ∷ !(Maybe StreamId)
+    -- ^ The stream ID of the first item that this operation will evaluate;
+    -- also see '_lstrLastEvalutedStreamId'.
+
+  , _lstLimit ∷ !(Maybe Int)
+    -- ^ The maximum number of streams to return.
+
+  , _lstTableName ∷ !(Maybe T.Text)
+    -- ^ If this parameter is provided, then only the streams associated with
+    -- this table are returned.
+
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+
+instance ToJSON ListStreams where
+  toJSON ListStreams{..} = object
+    [ "ExclusiveStartStreamId" .= _lstExclusiveStartStreamId
+    , "Limit" .= _lstLimit
+    , "TableName" .= _lstTableName
+    ]
+
+instance FromJSON ListStreams where
+  parseJSON =
+    withObject "ListStreams" $ \o →
+      pure ListStreams
+        ⊛ o .:? "ExclusiveStartStreamId"
+        ⊛ o .:? "Limit"
+        ⊛ o .:? "TableName"
+
+
+instance Monoid ListStreams where
+  mempty = listStreams
+  ls `mappend` ls' = ListStreams
+    { _lstExclusiveStartStreamId = _lstExclusiveStartStreamId ls <|> _lstExclusiveStartStreamId ls'
+    , _lstLimit = _lstLimit ls <|> _lstLimit ls'
+    , _lstTableName = _lstTableName ls <|> _lstTableName ls'
+    }
+
+-- | An empty 'ListStreams' request.
+--
+-- @
+-- myRequest = 'listStreams' & 'lstTableName' ?~ "UsersTable"
+-- @
+--
+listStreams ∷ ListStreams
+listStreams = ListStreams
+  { _lstExclusiveStartStreamId = Nothing
+  , _lstLimit = Nothing
+  , _lstTableName = Nothing
+  }
+
+-- | A lens for '_lstExclusiveStartStreamId'.
+--
+-- @
+-- lstExclusiveStartStreamId ∷ Lens' 'ListStreams' ('Maybe' 'StreamId')
+-- @
+--
+lstExclusiveStartStreamId
+  ∷ Functor f
+  ⇒ (Maybe StreamId → f (Maybe StreamId))
+  → ListStreams
+  → f ListStreams
+lstExclusiveStartStreamId i ListStreams{..} =
+  (\_lstExclusiveStartStreamId → ListStreams{..})
+    <$> i _lstExclusiveStartStreamId
+{-# INLINE lstExclusiveStartStreamId #-}
+
+-- | A lens for '_lstlimit'.
+--
+-- @
+-- lstLimit ∷ Lens' 'ListStreams' ('Maybe' 'Int')
+-- @
+--
+lstLimit
+  ∷ Functor f
+  ⇒ (Maybe Int → f (Maybe Int))
+  → ListStreams
+  → f ListStreams
+lstLimit i ListStreams{..} =
+  (\_lstLimit → ListStreams{..})
+    <$> i _lstLimit
+{-# INLINE lstLimit #-}
+
+-- | A lens for '_lstTableName'.
+--
+-- @
+-- lstTableName ∷ Lens' 'ListStreams' ('Maybe' 'T.Text')
+-- @
+--
+lstTableName
+  ∷ Functor f
+  ⇒ (Maybe T.Text → f (Maybe T.Text))
+  → ListStreams
+  → f ListStreams
+lstTableName i ListStreams{..} =
+  (\_lstTableName → ListStreams{..})
+    <$> i _lstTableName
+{-# INLINE lstTableName #-}
+
+
+data ListStreamsResponse
+  = ListStreamsResponse
+  { _lstrLastEvalutedStreamId ∷ !(Maybe StreamId)
+    -- ^ When empty, this indicates that there are no more streams to be
+    -- retrieved.
+
+  , _lstrStreamIds ∷ ![StreamId]
+    -- ^ A list of stream IDs associated with the current account and endpoint.
+  } deriving (Eq, Ord, Read, Show, Typeable)
+
+instance ToJSON ListStreamsResponse where
+  toJSON ListStreamsResponse{..} = object
+    [ "LastEvaluatedStreamId" .= _lstrLastEvalutedStreamId
+    , "StreamIds" .= _lstrStreamIds
+    ]
+
+instance FromJSON ListStreamsResponse where
+  parseJSON =
+    withObject "ListStreamsResponse" $ \o →
+      pure ListStreamsResponse
+        ⊛ o .:? "LastEvaluatedStreamId"
+        ⊛ o .:? "StreamIds" .!= []
+
+-- | A lens for '_lstrLastEvalutedStreamId'.
+--
+-- @
+-- lstrLastEvalutedStreamId ∷ Lens' 'ListStreamsResponse' ('Maybe' 'StreamId')
+-- @
+--
+lstrLastEvalutedStreamId
+  ∷ Functor f
+  ⇒ (Maybe StreamId → f (Maybe StreamId))
+  → ListStreamsResponse
+  → f ListStreamsResponse
+lstrLastEvalutedStreamId i ListStreamsResponse{..} =
+  (\_lstrLastEvalutedStreamId → ListStreamsResponse{..})
+    <$> i _lstrLastEvalutedStreamId
+{-# INLINE lstrLastEvalutedStreamId #-}
+
+-- | A lens for '_lstrStreamIds'.
+--
+-- @
+-- lstrStreamIds ∷ Lens' 'ListStreamsResponse' ['StreamId']
+-- @
+--
+lstrStreamIds
+  ∷ Functor f
+  ⇒ ([StreamId] → f [StreamId])
+  → ListStreamsResponse
+  → f ListStreamsResponse
+lstrStreamIds i ListStreamsResponse{..} =
+  (\_lstrStreamIds → ListStreamsResponse{..})
+    <$> i _lstrStreamIds
+{-# INLINE lstrStreamIds #-}
+
+instance ResponseConsumer r ListStreamsResponse where
+  type ResponseMetadata ListStreamsResponse = StreamsMetadata
+  responseConsumer _ = streamsResponseConsumer
+
+instance SignQuery ListStreams where
+  type ServiceConfiguration ListStreams = StreamsConfiguration
+  signQuery cmd = streamsSignQuery StreamsQuery
+    { _stqAction = ActionListStreams
+    , _stqBody = encode cmd
+    }
+
+instance Transaction ListStreams ListStreamsResponse
+
+instance AsMemoryResponse ListStreamsResponse where
+  type MemoryResponse ListStreamsResponse = ListStreamsResponse
+  loadToMemory = return
+
+instance ListResponse ListStreamsResponse StreamId where
+  listResponse = _lstrStreamIds
+
+instance IteratedTransaction ListStreams ListStreamsResponse where
+  nextIteratedRequest req@ListStreams{..} ListStreamsResponse{..} = do
+    lastEvaluatedStreamId ← _lstrLastEvalutedStreamId
+    return req
+      { _lstExclusiveStartStreamId = Just lastEvaluatedStreamId
+      }
diff --git a/src/Aws/DynamoDb/Streams/Core.hs b/src/Aws/DynamoDb/Streams/Core.hs
new file mode 100644
--- /dev/null
+++ b/src/Aws/DynamoDb/Streams/Core.hs
@@ -0,0 +1,593 @@
+-- Copyright (c) 2013-2014 PivotCloud, Inc.
+--
+-- Aws.DynamoDb.Streams.Core
+--
+-- Please feel free to contact us at licensing@pivotmail.com with any
+-- contributions, additions, or other feedback; we would love to hear from
+-- you.
+--
+-- Licensed 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.
+
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE UnicodeSyntax #-}
+
+-- |
+-- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc.
+-- License: Apache-2.0
+--
+module Aws.DynamoDb.Streams.Core
+( StreamsAction(..)
+, streamsActionToText
+, parseStreamsAction
+, streamsServiceEndpoint
+, StreamsMetadata(..)
+, stmAmzId2
+, stmRequestId
+, StreamsConfiguration(..)
+, stcRegion
+, StreamsQuery(..)
+, stqAction
+, stqBody
+, streamsSignQuery
+, StreamsResponseJsonErrorData(..)
+, srjedMessage
+, srjedJSON
+, StreamsErrorResponse(..)
+, _StreamsResponseJsonError
+, _StreamsErrorResponse
+, _StreamsOtherError
+, StreamsErrorResponseData(..)
+, sterdErrorCode
+, sterdErrorMessage
+, StreamsOtherErrorData(..)
+, stoeStatus
+, stoeMessage
+, jsonResponseConsumer
+, errorResponseConsumer
+, streamsResponseConsumer
+) where
+
+import Aws.Core
+import Aws.General
+
+import Control.Applicative
+import Control.Applicative.Unicode
+import Control.Exception
+import Control.Monad.Trans
+import Crypto.Hash
+import Data.Byteable
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Base16 as B16
+import qualified Data.ByteString.Char8 as B8
+import qualified Data.ByteString.Lazy as LB
+import Data.Aeson
+import qualified Data.CaseInsensitive as CI
+import Data.Conduit (($$+-))
+import Data.Conduit.Binary (sinkLbs)
+import Data.Function (on)
+import Data.IORef
+import Data.List
+import Data.Maybe
+import Data.Profunctor
+import Data.String
+import Data.Typeable
+import Control.Monad.Trans.Resource (throwM)
+import Data.Monoid
+import Data.Monoid.Unicode
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Network.HTTP.Types as HTTP
+import qualified Network.HTTP.Conduit as HTTP
+import Prelude.Unicode
+import qualified Text.Parser.Char as P
+import qualified Text.Parser.Combinators as P
+import Text.Parser.Combinators ((<?>))
+
+streamsTargetVersion ∷ IsString a ⇒ a
+streamsTargetVersion = "DynamoDBStreams_20120810"
+
+data StreamsAction
+  = ActionDescribeStream
+  | ActionGetRecords
+  | ActionGetShardIterator
+  | ActionListStreams
+  deriving (Eq, Ord, Enum, Bounded, Typeable, Read, Show)
+
+-- | Render a 'StreamsAction' as a string for use in an AWS request.
+--
+streamsActionToText
+  ∷ IsString s
+  ⇒ StreamsAction
+  → s
+streamsActionToText = \case
+  ActionDescribeStream → "DescribeStream"
+  ActionGetRecords → "GetRecords"
+  ActionGetShardIterator → "GetShardIterator"
+  ActionListStreams → "ListStreams"
+
+-- | Parse a 'StreamsAction'; this is the inverse of 'streamsActionToText'.
+--
+parseStreamsAction
+  ∷ P.CharParsing m
+  ⇒ m StreamsAction
+parseStreamsAction = P.choice parsers <?> "StreamsAction"
+  where
+    actionToParser a =
+      a <$ P.text (streamsActionToText a)
+
+    parsers = actionToParser <$>
+      [ ActionDescribeStream
+      , ActionGetRecords
+      , ActionGetShardIterator
+      , ActionListStreams
+      ]
+
+instance AwsType StreamsAction where
+  toText = streamsActionToText
+  parse = parseStreamsAction
+
+-- | Currently, only 'UsEast1' and 'EuWest1' are supported.
+--
+streamsServiceEndpoint
+  ∷ Region
+  → B8.ByteString
+streamsServiceEndpoint = \case
+  UsEast1 → "streams.preview-dynamodb.us-east-1.amazonaws.com"
+  EuWest1 → "streams.preview-dynamodb.eu-west-1.amazonaws.com"
+  region → error $ "Unsupported region: " ⊕ show region
+
+data StreamsMetadata
+  = StreamsMetadata
+  { _stmAmzId2 ∷ !(Maybe T.Text)
+  , _stmRequestId ∷ !(Maybe T.Text)
+  } deriving (Eq, Show)
+
+instance Loggable StreamsMetadata where
+  toLogText StreamsMetadata{..} =
+    "DynamoDb Streams: request ID="
+      ⊕ fromMaybe "<none>" _stmRequestId
+      ⊕ ", x-amz-id-2="
+      ⊕ fromMaybe "<none>" _stmAmzId2
+
+instance Monoid StreamsMetadata where
+  mempty = StreamsMetadata
+    { _stmAmzId2 = Nothing
+    , _stmRequestId = Nothing
+    }
+
+  sm `mappend` sm' = StreamsMetadata
+    { _stmAmzId2 = _stmAmzId2 sm <|> _stmAmzId2 sm'
+    , _stmRequestId = _stmRequestId sm <|> _stmRequestId sm'
+    }
+
+-- | A lens for '_stmAmzId2'.
+--
+-- @
+-- 'stmAmzId2' ∷ Lens' 'StreamsMetadata' ('Maybe' 'T.Text')
+-- @
+--
+stmAmzId2
+  ∷ Functor f
+  ⇒ (Maybe T.Text → f (Maybe T.Text))
+  → StreamsMetadata
+  → f StreamsMetadata
+stmAmzId2 i StreamsMetadata{..} =
+  (\_stmAmzId2 → StreamsMetadata{..})
+    <$> i _stmAmzId2
+{-# INLINE stmAmzId2 #-}
+
+-- | A lens for '_stmRequestId'.
+--
+-- @
+-- 'stmRequestId' ∷ Lens' 'StreamsMetadata' ('Maybe' 'T.Text')
+-- @
+--
+stmRequestId
+  ∷ Functor f
+  ⇒ (Maybe T.Text → f (Maybe T.Text))
+  → StreamsMetadata
+  → f StreamsMetadata
+stmRequestId i StreamsMetadata{..} =
+  (\_stmRequestId → StreamsMetadata{..})
+    <$> i _stmRequestId
+{-# INLINE stmRequestId #-}
+
+data StreamsConfiguration qt
+  = StreamsConfiguration
+  { _stcRegion ∷ !Region
+  } deriving (Eq, Show)
+
+-- | A lens for '_stcRegion'.
+--
+-- @
+-- 'stcRegion' ∷ Lens' 'StreamsConfiguration' 'Region'
+-- @
+--
+stcRegion
+  ∷ Functor f
+  ⇒ (Region → f Region)
+  → StreamsConfiguration qt
+  → f (StreamsConfiguration qt)
+stcRegion i StreamsConfiguration{..} =
+  (\_stcRegion → StreamsConfiguration{..})
+    <$> i _stcRegion
+{-# INLINE stcRegion #-}
+
+data StreamsQuery
+  = StreamsQuery
+  { _stqAction ∷ !StreamsAction
+  , _stqBody ∷ !LB.ByteString
+  } deriving (Eq, Show)
+
+-- | A lens for '_stqAction'.
+--
+-- @
+-- 'stqAction' ∷ Lens' 'StreamsQuery' 'StreamsAction'
+-- @
+--
+stqAction
+  ∷ Functor f
+  ⇒ (StreamsAction → f StreamsAction)
+  → StreamsQuery
+  → f StreamsQuery
+stqAction i StreamsQuery{..} =
+  (\_stqAction → StreamsQuery{..})
+    <$> i _stqAction
+{-# INLINE stqAction #-}
+
+-- | A lens for '_stqBody'.
+--
+-- @
+-- 'stqBody' ∷ Lens' 'StreamsQuery' 'LB.ByteString'
+-- @
+--
+stqBody
+  ∷ Functor f
+  ⇒ (LB.ByteString → f LB.ByteString)
+  → StreamsQuery
+  → f StreamsQuery
+stqBody i StreamsQuery{..} =
+  (\_stqBody → StreamsQuery{..})
+    <$> i _stqBody
+{-# INLINE stqBody #-}
+
+streamsTargetHeader
+  ∷ StreamsAction
+  → HTTP.Header
+streamsTargetHeader a =
+  ( "X-Amz-Target"
+  , streamsTargetVersion ⊕ "." ⊕ toText a
+  )
+
+-- | Creates a signed query.
+--
+-- Uses AWS Signature V4. All requests are POST requests
+-- with the signature placed in an HTTP header
+--
+streamsSignQuery
+  ∷ StreamsQuery
+  → StreamsConfiguration qt
+  → SignatureData
+  → SignedQuery
+streamsSignQuery StreamsQuery{..} StreamsConfiguration{..} sigData = SignedQuery
+  { sqMethod = Post
+  , sqProtocol = HTTP
+  , sqHost = host
+  , sqPort = 80
+  , sqPath = "/"
+  , sqQuery = []
+  , sqDate = Just $ signatureTime sigData
+  , sqAuthorization = Just auth
+  , sqContentType = Just "application/x-amz-json-1.0"
+  , sqContentMd5 = Nothing
+  , sqAmzHeaders = amzHeaders ⊕ maybe [] pure securityTokenHeader
+  , sqOtherHeaders = []
+  , sqBody = Just $ HTTP.RequestBodyLBS _stqBody
+  , sqStringToSign = canonicalRequest
+  }
+    where
+      credentials = signatureCredentials sigData
+      host = streamsServiceEndpoint _stcRegion
+      sigTime = fmtTime "%Y%m%dT%H%M%SZ" $ signatureTime sigData
+
+      -- for some reason AWS doesn't want the x-amz-security-token in the canonical request
+      amzHeaders =
+        [ ("x-amz-date", sigTime)
+        , streamsTargetHeader _stqAction
+        ]
+
+      securityTokenHeader =
+        ("x-amz-security-token",)
+          <$> iamToken credentials
+
+      canonicalHeaders =
+        sortBy (compare `on` fst) $
+          amzHeaders ⊕
+            [ ("host", host)
+            , ("content-type", "application/x-amz-json-1.0")
+            ]
+
+      canonicalRequest =
+        let bodyHash = B16.encode $ toBytes (hashlazy _stqBody :: Digest SHA256)
+            headers =
+              flip fmap canonicalHeaders $ \(a,b) →
+                [ CI.foldedCase a
+                , ":"
+                , b
+                ]
+        in
+          B.concat ∘ intercalate ["\n"] $
+            [ [ "POST" ]
+            , [ "/" ]
+            , [] -- query string
+            ] ⊕ headers ⊕
+              [ [] -- end headers
+              , intersperse ";" ((CI.foldedCase ∘ fst) <$> canonicalHeaders)
+              , [ bodyHash ]
+              ]
+
+      auth =
+        authorizationV4
+          sigData
+          HmacSHA256
+          (regionToText _stcRegion)
+          "dynamodb"
+          "content-type;host;x-amz-date;x-amz-target"
+          canonicalRequest
+
+
+data StreamsErrorResponseData
+  = StreamsErrorResponseData
+  { _sterdErrorCode ∷ !T.Text
+  , _sterdErrorMessage ∷ !T.Text
+  } deriving (Eq, Show, Typeable)
+
+-- | A lens for '_sterdErrorCode'.
+--
+-- @
+-- 'sterdErrorCode' ∷ Lens' 'StreamsErrorResponseData' 'T.Text'
+-- @
+--
+sterdErrorCode
+  ∷ Functor f
+  ⇒ (T.Text → f T.Text)
+  → StreamsErrorResponseData
+  → f StreamsErrorResponseData
+sterdErrorCode i StreamsErrorResponseData{..} =
+  (\_sterdErrorCode → StreamsErrorResponseData{..})
+    <$> i _sterdErrorCode
+{-# INLINE sterdErrorCode #-}
+
+-- | A lens for '_sterdErrorMessage'.
+--
+-- @
+-- 'sterdErrorMessage' ∷ Lens' 'StreamsErrorResponseData' 'T.Text'
+-- @
+--
+sterdErrorMessage
+  ∷ Functor f
+  ⇒ (T.Text → f T.Text)
+  → StreamsErrorResponseData
+  → f StreamsErrorResponseData
+sterdErrorMessage i StreamsErrorResponseData{..} =
+  (\_sterdErrorMessage → StreamsErrorResponseData{..})
+    <$> i _sterdErrorMessage
+{-# INLINE sterdErrorMessage #-}
+
+data StreamsOtherErrorData
+  = StreamsOtherErrorData
+  { _stoeStatus ∷ !HTTP.Status
+  , _stoeMessage ∷ !T.Text
+  } deriving (Eq, Show, Typeable)
+
+-- | A lens for '_stoeStatus'
+--
+-- @
+-- 'stoeStatus' ∷ Lens' 'StreamsOtherErrorData' 'HTTP.Status'
+-- @
+--
+stoeStatus
+  ∷ Functor f
+  ⇒ (HTTP.Status → f HTTP.Status)
+  → StreamsOtherErrorData
+  → f StreamsOtherErrorData
+stoeStatus i StreamsOtherErrorData{..} =
+  (\_stoeStatus → StreamsOtherErrorData{..})
+    <$> i _stoeStatus
+{-# INLINE stoeStatus #-}
+
+-- | A lens for '_stoeMessage'
+--
+-- @
+-- 'stoeMessage' ∷ Lens' 'StreamsOtherErrorData' 'T.Text'
+-- @
+--
+stoeMessage
+  ∷ Functor f
+  ⇒ (T.Text → f T.Text)
+  → StreamsOtherErrorData
+  → f StreamsOtherErrorData
+stoeMessage i StreamsOtherErrorData{..} =
+  (\_stoeMessage → StreamsOtherErrorData{..})
+    <$> i _stoeMessage
+{-# INLINE stoeMessage #-}
+
+data StreamsResponseJsonErrorData
+  = StreamsResponseJsonErrorData
+  { _srjedMessage ∷ !T.Text
+  , _srjedJSON ∷ !LB.ByteString
+  } deriving (Eq, Show, Typeable)
+
+-- | A lens for '_srjedMessage'.
+--
+-- @
+-- 'srjedMessage' ∷ Lens' 'StreamsResponseJsonErrorData' 'T.Text'
+-- @
+srjedMessage
+  ∷ Functor f
+  ⇒ (T.Text → f T.Text)
+  → StreamsResponseJsonErrorData
+  → f StreamsResponseJsonErrorData
+srjedMessage i StreamsResponseJsonErrorData{..} =
+  (\_srjedMessage → StreamsResponseJsonErrorData{..})
+    <$> i _srjedMessage
+
+-- | A lens for '_srjedJSON'.
+--
+-- @
+-- 'srjedJSON' ∷ Lens' 'StreamsResponseJsonErrorData' 'LB.ByteString'
+-- @
+srjedJSON
+  ∷ Functor f
+  ⇒ (LB.ByteString → f LB.ByteString)
+  → StreamsResponseJsonErrorData
+  → f StreamsResponseJsonErrorData
+srjedJSON i StreamsResponseJsonErrorData{..} =
+  (\_srjedJSON → StreamsResponseJsonErrorData{..})
+    <$> i _srjedJSON
+
+data StreamsErrorResponse
+  = StreamsResponseJsonError StreamsResponseJsonErrorData
+  | StreamsErrorResponse StreamsErrorResponseData
+  | StreamsOtherError StreamsOtherErrorData
+  deriving (Eq, Show, Typeable)
+
+-- | A prism for 'StreamsResponseJsonError'.
+--
+-- @
+-- '_StreamsResponseJsonError' ∷ Prism' 'StreamsErrorResponse' 'StreamsResponseJsonErrorData'
+-- @
+_StreamsResponseJsonError
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p StreamsResponseJsonErrorData (f StreamsResponseJsonErrorData)
+  → p StreamsErrorResponse (f StreamsErrorResponse)
+_StreamsResponseJsonError =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StreamsResponseJsonError e → Right e
+        e → Left e
+      fro = either pure (fmap StreamsResponseJsonError)
+{-# INLINE _StreamsResponseJsonError #-}
+
+-- | A prism for 'StreamsErrorResponse'.
+--
+-- @
+-- '_StreamsErrorResponse' ∷ Prism' 'StreamsErrorResponse' 'StreamsErrorResponseData'
+-- @
+_StreamsErrorResponse
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p StreamsErrorResponseData (f StreamsErrorResponseData)
+  → p StreamsErrorResponse (f StreamsErrorResponse)
+_StreamsErrorResponse =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StreamsErrorResponse e → Right e
+        e → Left e
+      fro = either pure (fmap StreamsErrorResponse)
+{-# INLINE _StreamsErrorResponse #-}
+
+-- | A prism for 'StreamsOtherError'.
+--
+-- @
+-- '_StreamsOtherError' ∷ Prism' 'StreamsErrorResponse' 'StreamsOtherErrorData'
+-- @
+_StreamsOtherError
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p StreamsOtherErrorData (f StreamsOtherErrorData)
+  → p StreamsErrorResponse (f StreamsErrorResponse)
+_StreamsOtherError =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StreamsOtherError e → Right e
+        e → Left e
+      fro = either pure (fmap StreamsOtherError)
+{-# INLINE _StreamsOtherError #-}
+
+instance Exception StreamsErrorResponse
+
+-- | This instance captures only the 'StreamsErrorResponse' constructor
+--
+instance FromJSON StreamsErrorResponse where
+  parseJSON =
+    withObject "StreamsErrorResponse" $ \o →
+      fmap StreamsErrorResponse $
+        pure StreamsErrorResponseData
+          ⊛ o .: "__type"
+          ⊛ o .: "message"
+
+
+-- | Create a complete 'HTTPResponseConsumer' for response types with an
+-- 'FromJSON' instance
+--
+jsonResponseConsumer
+  ∷ FromJSON α
+  ⇒ HTTPResponseConsumer α
+jsonResponseConsumer res = do
+  doc ← HTTP.responseBody res $$+- sinkLbs
+  case eitherDecode (if doc ≡ mempty then "{}" else doc) of
+    Left e → throwM $ StreamsResponseJsonError StreamsResponseJsonErrorData
+      { _srjedMessage = T.pack e
+      , _srjedJSON = doc
+      }
+    Right v → return v
+
+
+streamsResponseConsumer
+  ∷ FromJSON a
+  ⇒ IORef StreamsMetadata
+  → HTTPResponseConsumer a
+streamsResponseConsumer metadata resp = do
+
+  let headerString = fmap T.decodeUtf8 ∘ flip lookup (HTTP.responseHeaders resp)
+      amzId2 = headerString "x-amz-id-2"
+      requestId = headerString "x-amz-request-id"
+
+  liftIO $ tellMetadataRef metadata StreamsMetadata
+    { _stmAmzId2 = amzId2
+    , _stmRequestId = requestId
+    }
+
+  if HTTP.responseStatus resp ≥ HTTP.status400
+    then errorResponseConsumer resp
+    else jsonResponseConsumer resp
+
+errorResponseConsumer ∷ HTTPResponseConsumer a
+errorResponseConsumer resp = do
+  doc ← HTTP.responseBody resp $$+- sinkLbs
+  if HTTP.responseStatus resp ≡ HTTP.status400
+    then kinesisError doc
+    else throwM $ StreamsOtherError StreamsOtherErrorData
+        { _stoeStatus = HTTP.responseStatus resp
+        , _stoeMessage = T.decodeUtf8 $ LB.toStrict doc
+        }
+  where
+    kinesisError doc =
+      case eitherDecode doc of
+        Left e → throwM $ StreamsResponseJsonError StreamsResponseJsonErrorData
+          { _srjedMessage = T.pack e
+          , _srjedJSON = doc
+          }
+        Right a → throwM (a ∷ StreamsErrorResponse)
+
diff --git a/src/Aws/DynamoDb/Streams/Types.hs b/src/Aws/DynamoDb/Streams/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Aws/DynamoDb/Streams/Types.hs
@@ -0,0 +1,1637 @@
+-- Copyright (c) 2013-2014 PivotCloud, Inc.
+--
+-- Aws.DynamoDb.Streams.Types
+--
+-- Please feel free to contact us at licensing@pivotmail.com with any
+-- contributions, additions, or other feedback; we would love to hear from
+-- you.
+--
+-- Licensed 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.
+
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE UnicodeSyntax #-}
+
+-- |
+-- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc.
+-- License: Apache-2.0
+--
+module Aws.DynamoDb.Streams.Types
+( -- * Sequence Numbers
+  SequenceNumber
+, SequenceNumberRange(..)
+, sqnrStartingSequenceNumber
+, sqnrEndingSequenceNumber
+
+  -- * Shards
+, ShardId
+, Shard(..)
+  -- ** Lenses
+, shShardId
+, shParentShardId
+, shSequenceNumberRange
+
+  -- * Shard Iterators
+, ShardIterator
+, ShardIteratorType(..)
+  -- ** Prisms
+, _ShardIteratorTrimHorizon
+, _ShardIteratorLatest
+, _ShardIteratorAtSequenceNumber
+, _ShardIteratorAfterSequenceNumber
+
+  -- * Attribute Values
+, AttributeValue(..)
+  -- ** Prisms
+, _AVBin
+, _AVBool
+, _AVBinSet
+, _AVList
+, _AVMap
+, _AVNum
+, _AVNumSet
+, _AVNull
+, _AVString
+, _AVStringSet
+
+  -- * Key Schemas
+  -- ** Key Types
+, KeyType(..)
+, keyTypeToText
+  -- *** Prisms
+, _KeyTypeHash
+, _KeyTypeRange
+  -- ** Key Schema Elements
+, KeySchemaElement(..)
+  -- *** Lenses
+, kseAttributeName
+, kseKeyType
+
+  -- * Records
+  -- ** Stream View Types
+, StreamViewType(..)
+
+  -- *** Prisms
+, _StreamViewKeysOnly
+, _StreamViewNewImage
+, _StreamViewOldImage
+, _StreamViewNewAndOldImages
+
+  -- ** Stream Records
+, StreamRecord(..)
+
+  -- *** Lenses
+, strKeys
+, strNewImage
+, strOldImage
+, strSequenceNumber
+, strSizeBytes
+, strStreamViewType
+
+  -- ** Event Names
+, EventName(..)
+
+  -- *** Prisms
+, _EventInsert
+, _EventModify
+, _EventRemove
+
+  -- ** Records
+, Record(..)
+
+  -- *** Lenses
+, rAwsRegion
+, rStreamRecord
+, rEventId
+, rEventName
+, rEventSource
+, rEventVersion
+
+
+  -- * Stream Metadata
+  -- ** Stream Statuses
+, StreamStatus(..)
+
+  --- *** Prisms
+, _StatusEnabling
+, _StatusEnabled
+, _StatusDisabling
+, _StatusDisabled
+
+  -- ** Stream Description
+, StreamId
+, StreamDescription(..)
+  -- *** Lenses
+, sdCreationRequestDateTime
+, sdKeySchema
+, sdLastEvaluatedShardId
+, sdShards
+, sdStreamARN
+, sdStreamId
+, sdStreamStatus
+, sdStreamViewType
+, sdTableName
+) where
+
+import Aws.General
+import Control.Applicative
+import Control.Applicative.Unicode
+import Control.Monad.Unicode
+import Data.Aeson
+import Data.Aeson.Types
+import qualified Data.Attoparsec.Text as Atto
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Base64 as B64
+import Data.Foldable (asum)
+import qualified Data.Map as M
+import Data.Monoid.Unicode
+import Data.Profunctor
+import Data.Scientific
+import qualified Data.Set as S
+import Data.String
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import Data.Time
+import Data.Time.Clock.POSIX
+import Data.Traversable hiding (mapM)
+import Data.Typeable
+import Prelude.Unicode
+
+-- | Identifier for a shard.
+--
+-- Length constraints: @28 ≤ n ≤ 65@.
+--
+newtype ShardId
+  = ShardId
+  { _sidText ∷ T.Text
+  } deriving (Eq, Ord, Typeable, Show, Read)
+
+instance ToJSON ShardId where
+  toJSON = toJSON ∘ _sidText
+
+instance FromJSON ShardId where
+  parseJSON =
+    withText "ShardId" $
+      pure ∘ ShardId
+
+-- | Identifier for a record.
+--
+-- Length constraints: @21 ≤ n ≤ 40@
+--
+newtype SequenceNumber
+  = SequenceNumber
+  { _sqnText ∷ T.Text
+  } deriving (Eq, Ord, Typeable, Show, Read)
+
+instance ToJSON SequenceNumber where
+  toJSON = toJSON ∘ _sqnText
+
+instance FromJSON SequenceNumber where
+  parseJSON =
+    withText "SequenceNumber" $
+      pure ∘ SequenceNumber
+
+-- | The beginning and ending sequence numbers for the stream records contained
+-- within a shard.
+--
+data SequenceNumberRange
+  = SequenceNumberRange
+  { _sqnrStartingSequenceNumber ∷ !(Maybe SequenceNumber)
+  , _sqnrEndingSequenceNumber ∷ !(Maybe SequenceNumber)
+  } deriving (Eq, Ord, Typeable, Show, Read)
+
+-- | A lens for '_sqnrStartingSequenceNumber'.
+--
+-- @
+-- 'sqnrStartingSequenceNumber' ∷ Lens' 'SequenceNumberRange' ('Maybe' 'SequenceNumber')
+-- @
+--
+sqnrStartingSequenceNumber
+  ∷ Functor f
+  ⇒ (Maybe SequenceNumber → f (Maybe SequenceNumber))
+  → SequenceNumberRange
+  → f SequenceNumberRange
+sqnrStartingSequenceNumber i SequenceNumberRange{..} =
+  (\_sqnrStartingSequenceNumber → SequenceNumberRange{..})
+    <$> i _sqnrStartingSequenceNumber
+{-# INLINE sqnrStartingSequenceNumber #-}
+
+-- | A lens for '_sqnrEndingSequenceNumber'.
+--
+-- @
+-- 'sqnrEndingSequenceNumber' ∷ Lens' 'SequenceNumberRange' ('Maybe' 'SequenceNumber')
+-- @
+--
+sqnrEndingSequenceNumber
+  ∷ Functor f
+  ⇒ (Maybe SequenceNumber → f (Maybe SequenceNumber))
+  → SequenceNumberRange
+  → f SequenceNumberRange
+sqnrEndingSequenceNumber i SequenceNumberRange{..} =
+  (\_sqnrEndingSequenceNumber → SequenceNumberRange{..})
+    <$> i _sqnrEndingSequenceNumber
+{-# INLINE sqnrEndingSequenceNumber #-}
+
+instance ToJSON SequenceNumberRange where
+  toJSON SequenceNumberRange{..} = object
+    [ "StartingSequenceNumber" .= _sqnrStartingSequenceNumber
+    , "EndingSequenceNumber" .= _sqnrEndingSequenceNumber
+    ]
+
+instance FromJSON SequenceNumberRange where
+  parseJSON =
+    withObject "SequenceNumberRange" $ \o →
+      pure SequenceNumberRange
+        ⊛ o .:? "StartingSequenceNumber"
+        ⊛ o .:? "EndingSequenceNumber"
+
+-- | A uniquely identified group of stream records within a stream.
+--
+data Shard
+  = Shard
+  { _shShardId ∷ !(Maybe ShardId)
+    -- ^ The system-generated identifier for this shard
+  , _shParentShardId ∷ !(Maybe ShardId)
+    -- ^ The shard ID of this shard's parent
+  , _shSequenceNumberRange ∷ !(Maybe SequenceNumberRange)
+  -- ^ The range of possible sequence numbers for this shard
+  } deriving (Eq, Ord, Typeable, Show, Read)
+
+instance ToJSON Shard where
+  toJSON Shard{..} = object
+    [ "ShardId" .= _shShardId
+    , "ParentShardId" .= _shParentShardId
+    , "SequenceNumberRange" .= _shSequenceNumberRange
+    ]
+
+instance FromJSON Shard where
+  parseJSON =
+    withObject "Shard" $ \o →
+      pure Shard
+        ⊛ o .:? "ShardId"
+        ⊛ o .:? "ParentShardId"
+        ⊛ o .:? "SequenceNumberRange"
+
+-- | A lens for '_shShardId'.
+--
+-- @
+-- 'shshardId' ∷ Lens' 'Shard' ('Maybe' 'ShardId')
+-- @
+--
+shShardId
+  ∷ Functor f
+  ⇒ (Maybe ShardId → f (Maybe ShardId))
+  → Shard
+  → f Shard
+shShardId i Shard{..} =
+  (\_shShardId → Shard{..})
+    <$> i _shShardId
+{-# INLINE shShardId #-}
+
+-- | A lens for '_shParentShardId'.
+--
+-- @
+-- 'shParentShardId' ∷ Lens' 'Shard' ('Maybe' 'ShardId')
+-- @
+--
+shParentShardId
+  ∷ Functor f
+  ⇒ (Maybe ShardId → f (Maybe ShardId))
+  → Shard
+  → f Shard
+shParentShardId i Shard{..} =
+  (\_shParentShardId → Shard{..})
+    <$> i _shParentShardId
+{-# INLINE shParentShardId #-}
+
+-- | A lens for '_shSequenceNumberRange'.
+--
+-- @
+-- 'shSequenceNumberRange' ∷ Lens' 'Shard' ('Maybe' 'SequenceNumberRange')
+-- @
+--
+shSequenceNumberRange
+  ∷ Functor f
+  ⇒ (Maybe SequenceNumberRange → f (Maybe SequenceNumberRange))
+  → Shard
+  → f Shard
+shSequenceNumberRange i Shard{..} =
+  (\_shSequenceNumberRange → Shard{..})
+    <$> i _shSequenceNumberRange
+{-# INLINE shSequenceNumberRange #-}
+
+-- | Represents the data for an attribute.
+--
+data AttributeValue
+  = AVBin !B.ByteString
+    -- ^ Binary data will automatically be base64 marshalled
+  | AVBool !Bool
+  | AVBinSet !(S.Set B.ByteString)
+    -- ^ Binary data will automatically be base64 marshalled
+  | AVList ![AttributeValue]
+  | AVMap !(M.Map T.Text AttributeValue)
+  | AVNum !Scientific
+  | AVNumSet !(S.Set Scientific)
+  | AVNull !Bool -- TODO: why is this 'Bool'?
+  | AVString !T.Text
+  | AVStringSet !(S.Set T.Text)
+  deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON AttributeValue where
+  toJSON = \case
+    AVBin b → object ["B" .= T.decodeUtf8 (B64.encode b)]
+    AVBinSet bs → object ["BS" .= (T.decodeUtf8 ∘ B64.encode <$> S.toList bs)]
+    AVBool b → object ["BOOL" .= b]
+    AVList xs → object ["L" .= xs]
+    AVMap xs → object ["M" .= xs]
+    AVNum n → object ["N" .= T.pack (show n)]
+    AVNumSet ns → object ["NS" .= (T.pack ∘ show <$> S.toList ns)]
+    AVString s → object ["S" .= s]
+    AVNull b → object ["NULL" .= b]
+    AVStringSet ss → object ["SS" .= S.toList ss]
+
+instance FromJSON AttributeValue where
+  parseJSON =
+    withObject "AttributeValue" $ \o → asum
+      [ fmap AVBin ∘ parseBin =≪ o .: "B"
+      , fmap (AVBinSet ∘ S.fromList) ∘ mapM parseBin =≪ o .: "BS"
+      , AVBool <$> o .: "BOOL"
+      , AVList <$> o .: "L"
+      , AVMap <$> o .: "M"
+      , fmap AVNum ∘ parseScientific =≪ o .: "N"
+      , fmap (AVNumSet ∘ S.fromList) ∘ mapM parseScientific =≪ o .: "NS"
+      , AVString <$> o.: "S"
+      , AVStringSet ∘ S.fromList <$> o .: "SS"
+      , AVNull <$> o .: "NULL"
+      ]
+
+    where
+      parseBin =
+        either (fail ∘ ("parseBin failed: " ⊕)) pure
+          ∘ B64.decode
+          ∘ T.encodeUtf8
+
+      parseScientific (String str) =
+        either (fail ∘ ("parseScientific failed: " ⊕)) pure $
+          Atto.parseOnly Atto.scientific str
+      parseScientific (Number n) = pure n
+      parseScientific _ = fail "Unexpected JSON type in parseScientific"
+
+
+
+-- | A prism for 'AVBin'.
+--
+-- @
+-- '_AVBin' ∷ Prism' 'AttributeValue' 'B.ByteString'
+-- @
+_AVBin
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p B.ByteString (f B.ByteString)
+  → p AttributeValue (f AttributeValue)
+_AVBin =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVBin e → Right e
+        e → Left e
+      fro = either pure (fmap AVBin)
+{-# INLINE _AVBin #-}
+
+-- | A prism for 'AVBool'.
+--
+-- @
+-- '_AVBool' ∷ Prism' 'AttributeValue' 'Bool'
+-- @
+_AVBool
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p Bool (f Bool)
+  → p AttributeValue (f AttributeValue)
+_AVBool =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVBool e → Right e
+        e → Left e
+      fro = either pure (fmap AVBool)
+{-# INLINE _AVBool #-}
+
+-- | A prism for 'AVBinSet'.
+--
+-- @
+-- '_AVBinSet' ∷ Prism' 'AttributeValue' ('S.Set' 'B.ByteString')
+-- @
+_AVBinSet
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p (S.Set B.ByteString) (f (S.Set B.ByteString))
+  → p AttributeValue (f AttributeValue)
+_AVBinSet =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVBinSet e → Right e
+        e → Left e
+      fro = either pure (fmap AVBinSet)
+{-# INLINE _AVBinSet #-}
+
+-- | A prism for 'AVList'.
+--
+-- @
+-- '_AVList' ∷ Prism' 'AttributeValue' ['AttributeValue']
+-- @
+_AVList
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p [AttributeValue] (f [AttributeValue])
+  → p AttributeValue (f AttributeValue)
+_AVList =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVList e → Right e
+        e → Left e
+      fro = either pure (fmap AVList)
+{-# INLINE _AVList #-}
+
+-- | A prism for 'AVMap'.
+--
+-- @
+-- '_AVMap' ∷ Prism' 'AttributeValue' ('M.Map' 'T.Text' 'AttributeValue')
+-- @
+_AVMap
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p (M.Map T.Text AttributeValue) (f (M.Map T.Text AttributeValue))
+  → p AttributeValue (f AttributeValue)
+_AVMap =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVMap e → Right e
+        e → Left e
+      fro = either pure (fmap AVMap)
+{-# INLINE _AVMap #-}
+
+-- | A prism for 'AVNum'.
+--
+-- @
+-- '_AVNum' ∷ Prism' 'AttributeValue' 'Scientific'
+-- @
+_AVNum
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p Scientific (f Scientific)
+  → p AttributeValue (f AttributeValue)
+_AVNum =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVNum e → Right e
+        e → Left e
+      fro = either pure (fmap AVNum)
+{-# INLINE _AVNum #-}
+
+-- | A prism for 'AVNumSet'.
+--
+-- @
+-- '_AVNumSet' ∷ Prism' 'AttributeValue' ('S.Set' 'Scientific')
+-- @
+_AVNumSet
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p (S.Set Scientific) (f (S.Set Scientific))
+  → p AttributeValue (f AttributeValue)
+_AVNumSet =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVNumSet e → Right e
+        e → Left e
+      fro = either pure (fmap AVNumSet)
+{-# INLINE _AVNumSet #-}
+
+-- | A prism for 'AVNull'.
+--
+-- @
+-- '_AVNull' ∷ Prism' 'AttributeValue' 'Bool'
+-- @
+_AVNull
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p Bool (f Bool)
+  → p AttributeValue (f AttributeValue)
+_AVNull =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVNull e → Right e
+        e → Left e
+      fro = either pure (fmap AVNull)
+{-# INLINE _AVNull #-}
+
+-- | A prism for 'AVString'.
+--
+-- @
+-- '_AVString' ∷ Prism' 'AttributeValue' 'T.Text'
+-- @
+_AVString
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p T.Text (f T.Text)
+  → p AttributeValue (f AttributeValue)
+_AVString =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVString e → Right e
+        e → Left e
+      fro = either pure (fmap AVString)
+{-# INLINE _AVString #-}
+
+-- | A prism for 'AVStringSet'.
+--
+-- @
+-- '_AVStringSet' ∷ Prism' 'AttributeValue' ('S.Set' 'T.Text')
+-- @
+_AVStringSet
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p (S.Set T.Text) (f (S.Set T.Text))
+  → p AttributeValue (f AttributeValue)
+_AVStringSet =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        AVStringSet e → Right e
+        e → Left e
+      fro = either pure (fmap AVStringSet)
+{-# INLINE _AVStringSet #-}
+
+data KeyType
+  = KeyTypeHash
+  | KeyTypeRange
+  deriving (Eq, Ord, Enum, Show, Read, Typeable)
+
+keyTypeToText
+  ∷ IsString s
+  ⇒ KeyType
+  → s
+keyTypeToText = \case
+  KeyTypeHash → "HASH"
+  KeyTypeRange → "RANGE"
+
+instance ToJSON KeyType where
+  toJSON = keyTypeToText
+
+instance FromJSON KeyType where
+  parseJSON =
+    parseEnum "KeyType" keyTypeToText
+      [ KeyTypeHash
+      , KeyTypeRange
+      ]
+
+parseEnum
+  ∷ String
+  → (α → T.Text)
+  → [α]
+  → Value
+  → Parser α
+parseEnum name render opts =
+  withText name $ \str →
+    asum $ parser str <$> opts
+
+  where
+    parser str o =
+      o <$ if render o ≡ str
+        then pure o
+        else empty
+
+-- | A prism for 'KeyTypeHash'.
+--
+-- @
+-- '_KeyTypeHash' ∷ Prism' 'KeyType' ()
+-- @
+_KeyTypeHash
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p KeyType (f KeyType)
+_KeyTypeHash =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        KeyTypeHash → Right ()
+        e → Left e
+      fro = either pure (const $ pure KeyTypeHash)
+{-# INLINE _KeyTypeHash #-}
+
+-- | A prism for 'KeyTypeRange'.
+--
+-- @
+-- '_KeyTypeRange' ∷ Prism' 'KeyType' ()
+-- @
+_KeyTypeRange
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p KeyType (f KeyType)
+_KeyTypeRange =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        KeyTypeRange → Right ()
+        e → Left e
+      fro = either pure (const $ pure KeyTypeRange)
+{-# INLINE _KeyTypeRange #-}
+
+
+data KeySchemaElement
+  = KeySchemaElement
+  { _kseAttributeName ∷ !T.Text
+  , _kseKeyType ∷ !KeyType
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON KeySchemaElement where
+  toJSON KeySchemaElement{..} = object
+    [ "AttributeName" .= _kseAttributeName
+    , "KeyType" .= _kseKeyType
+    ]
+
+instance FromJSON KeySchemaElement where
+  parseJSON =
+    withObject "KeySchemaElement" $ \o →
+      pure KeySchemaElement
+        ⊛ o .: "AttributeName"
+        ⊛ o .: "KeyType"
+
+-- | A lens for '_kseAttributeName'.
+--
+-- @
+-- 'kseAttributeName' ∷ Lens' 'KeySchemaElement' 'T.Text'
+-- @
+--
+kseAttributeName
+  ∷ Functor f
+  ⇒ (T.Text → f T.Text)
+  → KeySchemaElement
+  → f KeySchemaElement
+kseAttributeName i KeySchemaElement{..} =
+  (\_kseAttributeName → KeySchemaElement{..})
+    <$> i _kseAttributeName
+{-# INLINE kseAttributeName #-}
+
+-- | A lens for '_kseKeyType'.
+--
+-- @
+-- 'kseKeyType' ∷ Lens' 'KeySchemaElement' 'KeyType'
+-- @
+--
+kseKeyType
+  ∷ Functor f
+  ⇒ (KeyType → f KeyType)
+  → KeySchemaElement
+  → f KeySchemaElement
+kseKeyType i KeySchemaElement{..} =
+  (\_kseKeyType → KeySchemaElement{..})
+    <$> i _kseKeyType
+{-# INLINE kseKeyType #-}
+
+
+data StreamViewType
+  = StreamViewKeysOnly
+    -- ^ only the key attributes of the modified item
+  | StreamViewNewImage
+    -- ^ the entire item, as it appears after it was modified
+  | StreamViewOldImage
+    -- ^ the entire item, as it appeared before it was modified
+  | StreamViewNewAndOldImages
+    -- ^ both the new and the old images of the item
+  deriving (Eq, Ord, Enum, Show, Read, Typeable)
+
+streamViewTypeToText
+  ∷ IsString s
+  ⇒ StreamViewType
+  → s
+streamViewTypeToText = \case
+  StreamViewKeysOnly → "KEYS_ONLY"
+  StreamViewNewImage → "NEW_IMAGE"
+  StreamViewOldImage → "OLD_IMAGE"
+  StreamViewNewAndOldImages → "NEW_AND_OLD_IMAGES"
+
+instance ToJSON StreamViewType where
+  toJSON = streamViewTypeToText
+
+instance FromJSON StreamViewType where
+  parseJSON =
+    parseEnum "StreamViewType" streamViewTypeToText
+      [ StreamViewKeysOnly
+      , StreamViewNewImage
+      , StreamViewOldImage
+      , StreamViewNewAndOldImages
+      ]
+
+-- | A prism for 'StreamViewKeysOnly'.
+--
+-- @
+-- '_StreamViewKeysOnly' ∷ Prism' 'StreamViewType' ()
+-- @
+_StreamViewKeysOnly
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamViewType (f StreamViewType)
+_StreamViewKeysOnly =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StreamViewKeysOnly → Right ()
+        e → Left e
+      fro = either pure (const $ pure StreamViewKeysOnly)
+{-# INLINE _StreamViewKeysOnly #-}
+
+-- | A prism for 'StreamViewNewImage'.
+--
+-- @
+-- '_StreamViewNewImage' ∷ Prism' 'StreamViewType' ()
+-- @
+_StreamViewNewImage
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamViewType (f StreamViewType)
+_StreamViewNewImage =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StreamViewNewImage → Right ()
+        e → Left e
+      fro = either pure (const $ pure StreamViewNewImage)
+{-# INLINE _StreamViewNewImage #-}
+
+-- | A prism for 'StreamViewOldImage'.
+--
+-- @
+-- '_StreamViewOldImage' ∷ Prism' 'StreamViewType' ()
+-- @
+_StreamViewOldImage
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamViewType (f StreamViewType)
+_StreamViewOldImage =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StreamViewOldImage → Right ()
+        e → Left e
+      fro = either pure (const $ pure StreamViewOldImage)
+{-# INLINE _StreamViewOldImage #-}
+
+-- | A prism for 'StreamViewNewAndOldImages'.
+--
+-- @
+-- '_StreamViewNewAndOldImages' ∷ Prism' 'StreamViewType' ()
+-- @
+_StreamViewNewAndOldImages
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamViewType (f StreamViewType)
+_StreamViewNewAndOldImages =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StreamViewNewAndOldImages → Right ()
+        e → Left e
+      fro = either pure (const $ pure StreamViewNewAndOldImages)
+{-# INLINE _StreamViewNewAndOldImages #-}
+
+data StreamRecord
+  = StreamRecord
+  { _strKeys ∷ !(Maybe (M.Map T.Text AttributeValue))
+  , _strNewImage ∷ !(Maybe (M.Map T.Text AttributeValue))
+  , _strOldImage ∷ !(Maybe (M.Map T.Text AttributeValue))
+  , _strSequenceNumber ∷ !(Maybe SequenceNumber)
+  , _strSizeBytes ∷ !(Maybe Integer)
+  , _strStreamViewType ∷ !(Maybe StreamViewType)
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON StreamRecord where
+  toJSON StreamRecord{..} = object
+    [ "Keys" .= _strKeys
+    , "NewImage" .= _strNewImage
+    , "OldImage" .= _strOldImage
+    , "SequenceNumber" .= _strSequenceNumber
+    , "SizeBytes" .= _strSizeBytes
+    , "StreamViewType" .= _strStreamViewType
+    ]
+
+instance FromJSON StreamRecord where
+  parseJSON =
+    withObject "StreamRecord" $ \o →
+      pure StreamRecord
+        ⊛ o .:? "Keys"
+        ⊛ o .:? "NewImage"
+        ⊛ o .:? "OldImage"
+        ⊛ o .:? "SequenceNumber"
+        ⊛ o .:? "SizeBytes"
+        ⊛ o .:? "StreamViewType"
+
+-- | A lens for '_strKeys'.
+--
+-- @
+-- 'strKeys' ∷ Lens' 'StreamRecord' ('Maybe' ('M.Map' 'T.Text' 'AttributeValue'))
+-- @
+--
+strKeys
+  ∷ Functor f
+  ⇒ (Maybe (M.Map T.Text AttributeValue) → f (Maybe (M.Map T.Text AttributeValue)))
+  → StreamRecord
+  → f StreamRecord
+strKeys i StreamRecord{..} =
+  (\_strKeys → StreamRecord{..})
+    <$> i _strKeys
+{-# INLINE strKeys #-}
+
+-- | A lens for '_strNewImage'.
+--
+-- @
+-- 'strNewImage' ∷ Lens' 'StreamRecord' ('Maybe' ('M.Map' 'T.Text' 'AttributeValue'))
+-- @
+--
+strNewImage
+  ∷ Functor f
+  ⇒ (Maybe (M.Map T.Text AttributeValue) → f (Maybe (M.Map T.Text AttributeValue)))
+  → StreamRecord
+  → f StreamRecord
+strNewImage i StreamRecord{..} =
+  (\_strNewImage → StreamRecord{..})
+    <$> i _strNewImage
+{-# INLINE strNewImage #-}
+
+-- | A lens for '_strOldImage'.
+--
+-- @
+-- 'strOldImage' ∷ Lens' 'StreamRecord' ('Maybe' ('M.Map' 'T.Text' 'AttributeValue'))
+-- @
+--
+strOldImage
+  ∷ Functor f
+  ⇒ (Maybe (M.Map T.Text AttributeValue) → f (Maybe (M.Map T.Text AttributeValue)))
+  → StreamRecord
+  → f StreamRecord
+strOldImage i StreamRecord{..} =
+  (\_strOldImage → StreamRecord{..})
+    <$> i _strOldImage
+{-# INLINE strOldImage #-}
+
+-- | A lens for '_strSequenceNumber'.
+--
+-- @
+-- 'strSequenceNumber' ∷ Lens' 'StreamRecord' ('Maybe' 'SequenceNumber')
+-- @
+--
+strSequenceNumber
+  ∷ Functor f
+  ⇒ (Maybe SequenceNumber → f (Maybe SequenceNumber))
+  → StreamRecord
+  → f StreamRecord
+strSequenceNumber i StreamRecord{..} =
+  (\_strSequenceNumber → StreamRecord{..})
+    <$> i _strSequenceNumber
+{-# INLINE strSequenceNumber #-}
+
+-- | A lens for '_strSizeBytes'.
+--
+-- @
+-- 'strSizeBytes' ∷ Lens' 'StreamRecord' ('Maybe' 'Integer')
+-- @
+--
+strSizeBytes
+  ∷ Functor f
+  ⇒ (Maybe Integer → f (Maybe Integer))
+  → StreamRecord
+  → f StreamRecord
+strSizeBytes i StreamRecord{..} =
+  (\_strSizeBytes → StreamRecord{..})
+    <$> i _strSizeBytes
+{-# INLINE strSizeBytes #-}
+
+-- | A lens for '_strStreamViewType'.
+--
+-- @
+-- 'strStreamViewType' ∷ Lens' 'StreamRecord' ('Maybe' 'StreamViewType')
+-- @
+--
+strStreamViewType
+  ∷ Functor f
+  ⇒ (Maybe StreamViewType → f (Maybe StreamViewType))
+  → StreamRecord
+  → f StreamRecord
+strStreamViewType i StreamRecord{..} =
+  (\_strStreamViewType → StreamRecord{..})
+    <$> i _strStreamViewType
+{-# INLINE strStreamViewType #-}
+
+
+-- | Identifier for a stream.
+--
+-- Length constraints: @56 ≤ n ≤ 128@.
+--
+newtype StreamId
+  = StreamId
+  { _stidText ∷ T.Text
+  } deriving (Eq, Ord, Typeable, Show, Read)
+
+instance ToJSON StreamId where
+  toJSON = toJSON ∘ _stidText
+
+instance FromJSON StreamId where
+  parseJSON =
+    withText "StreamId" $
+      pure ∘ StreamId
+
+data StreamStatus
+  = StatusEnabling
+    -- ^ DynamoDB Streams is currently being enabled on the table
+  | StatusEnabled
+    -- ^ The stream is enabled
+  | StatusDisabling
+    -- ^ DynamoDB Streams is currently being disabled on the table
+  | StatusDisabled
+    -- ^ The stream is disabled
+  deriving (Eq, Ord, Enum, Show, Read, Typeable)
+
+streamStatusToText
+  ∷ IsString a
+  ⇒ StreamStatus
+  → a
+streamStatusToText = \case
+  StatusEnabling → "ENABLING"
+  StatusEnabled → "ENABLED"
+  StatusDisabling → "DISABLING"
+  StatusDisabled → "DISABLED"
+
+instance ToJSON StreamStatus where
+  toJSON = streamStatusToText
+
+instance FromJSON StreamStatus where
+  parseJSON =
+    parseEnum "StreamStatus" streamStatusToText
+      [ StatusEnabling
+      , StatusEnabled
+      , StatusDisabling
+      , StatusDisabled
+      ]
+
+-- | A prism for 'StatusEnabling'.
+--
+-- @
+-- '_StatusEnabling' ∷ Prism' 'StreamStatus' ()
+-- @
+_StatusEnabling
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamStatus (f StreamStatus)
+_StatusEnabling =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StatusEnabling → Right ()
+        e → Left e
+      fro = either pure (const $ pure StatusEnabling)
+{-# INLINE _StatusEnabling #-}
+
+-- | A prism for 'StatusEnabled'.
+--
+-- @
+-- '_StatusEnabled' ∷ Prism' 'StreamStatus' ()
+-- @
+_StatusEnabled
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamStatus (f StreamStatus)
+_StatusEnabled =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StatusEnabled → Right ()
+        e → Left e
+      fro = either pure (const $ pure StatusEnabled)
+{-# INLINE _StatusEnabled #-}
+
+-- | A prism for 'StatusDisabling'.
+--
+-- @
+-- '_StatusDisabling' ∷ Prism' 'StreamStatus' ()
+-- @
+_StatusDisabling
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamStatus (f StreamStatus)
+_StatusDisabling =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StatusDisabling → Right ()
+        e → Left e
+      fro = either pure (const $ pure StatusDisabling)
+{-# INLINE _StatusDisabling #-}
+
+-- | A prism for 'StatusDisabled'.
+--
+-- @
+-- '_StatusDisabled' ∷ Prism' 'StreamStatus' ()
+-- @
+_StatusDisabled
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p StreamStatus (f StreamStatus)
+_StatusDisabled =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        StatusDisabled → Right ()
+        e → Left e
+      fro = either pure (const $ pure StatusDisabled)
+{-# INLINE _StatusDisabled #-}
+
+
+data StreamDescription
+  = StreamDescription
+  { _sdCreationRequestDateTime ∷ !(Maybe UTCTime)
+  , _sdKeySchema ∷ ![KeySchemaElement]
+  , _sdLastEvaluatedShardId ∷ !(Maybe ShardId)
+  , _sdShards ∷ ![Shard]
+  , _sdStreamARN ∷ !(Maybe T.Text)
+  , _sdStreamId ∷ !(Maybe StreamId)
+  , _sdStreamStatus ∷ !(Maybe StreamStatus)
+  , _sdStreamViewType ∷ !(Maybe StreamViewType)
+  , _sdTableName ∷ !(Maybe T.Text)
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON StreamDescription where
+  toJSON StreamDescription{..} = object
+    [ "CreationRequestDateTime" .= (Number ∘ fromInteger ∘ round ∘ utcTimeToPOSIXSeconds <$> _sdCreationRequestDateTime)
+    , "KeySchema" .= _sdKeySchema
+    , "Shards" .= _sdShards
+    , "StreamARN" .= _sdStreamARN
+    , "StreamId" .= _sdStreamId
+    , "StreamStatus" .= _sdStreamStatus
+    , "StreamViewType" .= _sdStreamViewType
+    , "TableName" .= _sdTableName
+    ]
+
+instance FromJSON StreamDescription where
+  parseJSON =
+    withObject "StreamDescription" $ \o →
+      pure StreamDescription
+        ⊛ (fmap (posixSecondsToUTCTime ∘ fromInteger) <$> o .:? "CreationRequestDateTime")
+        ⊛ o .:? "KeySchema" .!= []
+        ⊛ o .:? "LastEvaluatedShardId"
+        ⊛ o .:? "Shards" .!= []
+        ⊛ o .:? "StreamARN"
+        ⊛ o .:? "StreamId"
+        ⊛ o .:? "StreamStatus"
+        ⊛ o .:? "StreamViewType"
+        ⊛ o .:? "TableName"
+
+-- | A lens for '_sdCreationRequestDateTime'.
+--
+-- @
+-- 'sdCreationRequestDateTime' ∷ Lens' 'StreamDescription' ('Maybe' 'UTCTime')
+-- @
+--
+sdCreationRequestDateTime
+  ∷ Functor f
+  ⇒ (Maybe UTCTime → f (Maybe UTCTime))
+  → StreamDescription
+  → f StreamDescription
+sdCreationRequestDateTime i StreamDescription{..} =
+  (\_sdCreationRequestDateTime → StreamDescription{..})
+    <$> i _sdCreationRequestDateTime
+{-# INLINE sdCreationRequestDateTime #-}
+
+-- | A lens for '_sdKeySchema'.
+--
+-- @
+-- 'sdKeySchema' ∷ Lens' 'StreamDescription' ['KeySchemaElement']
+-- @
+--
+sdKeySchema
+  ∷ Functor f
+  ⇒ ([KeySchemaElement] → f [KeySchemaElement])
+  → StreamDescription
+  → f StreamDescription
+sdKeySchema i StreamDescription{..} =
+  (\_sdKeySchema → StreamDescription{..})
+    <$> i _sdKeySchema
+{-# INLINE sdKeySchema #-}
+
+-- | A lens for '_sdLastEvaluatedShardId'.
+--
+-- @
+-- 'sdLastEvaluatedShardId' ∷ Lens' 'StreamDescription' ('Maybe' 'ShardId')
+-- @
+--
+sdLastEvaluatedShardId
+  ∷ Functor f
+  ⇒ (Maybe ShardId → f (Maybe ShardId))
+  → StreamDescription
+  → f StreamDescription
+sdLastEvaluatedShardId i StreamDescription{..} =
+  (\_sdLastEvaluatedShardId → StreamDescription{..})
+    <$> i _sdLastEvaluatedShardId
+{-# INLINE sdLastEvaluatedShardId #-}
+
+-- | A lens for '_sdShards'.
+--
+-- @
+-- 'sdShards' ∷ Lens' 'StreamDescription' ['Shard']
+-- @
+--
+sdShards
+  ∷ Functor f
+  ⇒ ([Shard] → f [Shard])
+  → StreamDescription
+  → f StreamDescription
+sdShards i StreamDescription{..} =
+  (\_sdShards → StreamDescription{..})
+    <$> i _sdShards
+{-# INLINE sdShards #-}
+
+-- | A lens for '_sdStreamARN'.
+--
+-- @
+-- 'sdStreamARN' ∷ Lens' 'StreamDescription' ('Maybe' 'T.Text')
+-- @
+--
+sdStreamARN
+  ∷ Functor f
+  ⇒ (Maybe T.Text → f (Maybe T.Text))
+  → StreamDescription
+  → f StreamDescription
+sdStreamARN i StreamDescription{..} =
+  (\_sdStreamARN → StreamDescription{..})
+    <$> i _sdStreamARN
+{-# INLINE sdStreamARN #-}
+
+-- | A lens for '_sdStreamId'.
+--
+-- @
+-- 'sdStreamId' ∷ Lens' 'StreamDescription' ('Maybe' 'StreamId')
+-- @
+--
+sdStreamId
+  ∷ Functor f
+  ⇒ (Maybe StreamId → f (Maybe StreamId))
+  → StreamDescription
+  → f StreamDescription
+sdStreamId i StreamDescription{..} =
+  (\_sdStreamId → StreamDescription{..})
+    <$> i _sdStreamId
+{-# INLINE sdStreamId #-}
+
+-- | A lens for '_sdStreamStatus'.
+--
+-- @
+-- 'sdStreamStatus' ∷ Lens' 'StreamDescription' ('Maybe' 'StreamStatus')
+-- @
+--
+sdStreamStatus
+  ∷ Functor f
+  ⇒ (Maybe StreamStatus → f (Maybe StreamStatus))
+  → StreamDescription
+  → f StreamDescription
+sdStreamStatus i StreamDescription{..} =
+  (\_sdStreamStatus → StreamDescription{..})
+    <$> i _sdStreamStatus
+{-# INLINE sdStreamStatus #-}
+
+-- | A lens for '_sdStreamViewType'.
+--
+-- @
+-- 'sdStreamViewType' ∷ Lens' 'StreamDescription' ('Maybe' 'StreamViewType')
+-- @
+--
+sdStreamViewType
+  ∷ Functor f
+  ⇒ (Maybe StreamViewType → f (Maybe StreamViewType))
+  → StreamDescription
+  → f StreamDescription
+sdStreamViewType i StreamDescription{..} =
+  (\_sdStreamViewType → StreamDescription{..})
+    <$> i _sdStreamViewType
+{-# INLINE sdStreamViewType #-}
+
+-- | A lens for '_sdTableName'.
+--
+-- @
+-- 'sdTableName' ∷ Lens' 'StreamDescription' ('Maybe' 'T.Text')
+-- @
+--
+sdTableName
+  ∷ Functor f
+  ⇒ (Maybe T.Text → f (Maybe T.Text))
+  → StreamDescription
+  → f StreamDescription
+sdTableName i StreamDescription{..} =
+  (\_sdTableName → StreamDescription{..})
+    <$> i _sdTableName
+{-# INLINE sdTableName #-}
+
+
+-- | A globally unique identifier for the event that was recorded.
+--
+newtype EventId
+  = EventId
+  { _eidText ∷ T.Text
+  } deriving (Eq, Ord, Typeable, Show, Read)
+
+instance ToJSON EventId where
+  toJSON = toJSON ∘ _eidText
+
+instance FromJSON EventId where
+  parseJSON =
+    withText "EventId" $
+      pure ∘ EventId
+
+data EventName
+  = EventInsert
+  | EventModify
+  | EventRemove
+  deriving (Eq, Ord, Enum, Typeable, Show, Read)
+
+eventNameToText
+  ∷ IsString s
+  ⇒ EventName
+  → s
+eventNameToText = \case
+  EventInsert → "INSERT"
+  EventModify → "MODIFY"
+  EventRemove → "REMOVE"
+
+instance ToJSON EventName where
+  toJSON = eventNameToText
+
+instance FromJSON EventName where
+  parseJSON =
+    parseEnum "EventName" eventNameToText
+      [ EventInsert
+      , EventModify
+      , EventRemove
+      ]
+
+-- | A prism for 'EventInsert'.
+--
+-- @
+-- '_EventInsert' ∷ Prism' 'EventName' ()
+-- @
+_EventInsert
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p EventName (f EventName)
+_EventInsert =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        EventInsert → Right ()
+        e → Left e
+      fro = either pure (const $ pure EventInsert)
+{-# INLINE _EventInsert #-}
+
+-- | A prism for 'EventModify'.
+--
+-- @
+-- '_EventModify' ∷ Prism' 'EventName' ()
+-- @
+_EventModify
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p EventName (f EventName)
+_EventModify =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        EventModify → Right ()
+        e → Left e
+      fro = either pure (const $ pure EventModify)
+{-# INLINE _EventModify #-}
+
+-- | A prism for 'EventRemove'.
+--
+-- @
+-- '_EventRemove' ∷ Prism' 'EventName' ()
+-- @
+_EventRemove
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p EventName (f EventName)
+_EventRemove =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        EventRemove → Right ()
+        e → Left e
+      fro = either pure (const $ pure EventRemove)
+{-# INLINE _EventRemove #-}
+
+
+data Record
+  = Record
+  { _rAwsRegion ∷ !(Maybe Region)
+  , _rStreamRecord ∷ !(Maybe StreamRecord)
+  , _rEventId ∷ !(Maybe EventId)
+  , _rEventName ∷ !(Maybe EventName)
+  , _rEventSource ∷ !(Maybe T.Text)
+  , _rEventVersion ∷ !(Maybe T.Text)
+  } deriving (Eq, Ord, Show, Read, Typeable)
+
+instance ToJSON Record where
+  toJSON Record{..} = object
+    [ "awsRegion" .= (String ∘ regionToText <$> _rAwsRegion)
+    , "dynamodb" .= _rStreamRecord
+    , "eventID" .= _rEventId
+    , "eventName" .= _rEventName
+    , "eventSource" .= _rEventSource
+    , "eventVersion" .= _rEventVersion
+    ]
+
+instance FromJSON Record where
+  parseJSON =
+    withObject "Record" $ \o →
+      pure Record
+        ⊛ (traverse (either fail pure ∘ fromText) =≪ o .:? "awsRegion")
+        ⊛ o .:? "dynamodb"
+        ⊛ o .:? "eventID"
+        ⊛ o .:? "eventName"
+        ⊛ o .:? "eventSource"
+        ⊛ o .:? "eventVersion"
+
+-- | A lens for '_rAwsRegion'.
+--
+-- @
+-- 'rAwsRegion' ∷ Lens' 'Record' ('Maybe' 'Region')
+-- @
+--
+rAwsRegion
+  ∷ Functor f
+  ⇒ (Maybe Region → f (Maybe Region))
+  → Record
+  → f Record
+rAwsRegion i Record{..} =
+  (\_rAwsRegion → Record{..})
+    <$> i _rAwsRegion
+{-# INLINE rAwsRegion #-}
+
+-- | A lens for '_rStreamRecord'.
+--
+-- @
+-- 'rStreamRecord' ∷ Lens' 'Record' ('Maybe' 'StreamRecord')
+-- @
+--
+rStreamRecord
+  ∷ Functor f
+  ⇒ (Maybe StreamRecord → f (Maybe StreamRecord))
+  → Record
+  → f Record
+rStreamRecord i Record{..} =
+  (\_rStreamRecord → Record{..})
+    <$> i _rStreamRecord
+{-# INLINE rStreamRecord #-}
+
+-- | A lens for '_rEventId'.
+--
+-- @
+-- 'rEventId' ∷ Lens' 'Record' ('Maybe' 'EventId')
+-- @
+--
+rEventId
+  ∷ Functor f
+  ⇒ (Maybe EventId → f (Maybe EventId))
+  → Record
+  → f Record
+rEventId i Record{..} =
+  (\_rEventId → Record{..})
+    <$> i _rEventId
+{-# INLINE rEventId #-}
+
+-- | A lens for '_rEventName'.
+--
+-- @
+-- 'rEventName' ∷ Lens' 'Record' ('Maybe' 'EventName')
+-- @
+--
+rEventName
+  ∷ Functor f
+  ⇒ (Maybe EventName → f (Maybe EventName))
+  → Record
+  → f Record
+rEventName i Record{..} =
+  (\_rEventName → Record{..})
+    <$> i _rEventName
+{-# INLINE rEventName #-}
+
+-- | A lens for '_rEventSource'.
+--
+-- @
+-- 'rEventSource' ∷ Lens' 'Record' ('Maybe' 'T.Text')
+-- @
+--
+rEventSource
+  ∷ Functor f
+  ⇒ (Maybe T.Text → f (Maybe T.Text))
+  → Record
+  → f Record
+rEventSource i Record{..} =
+  (\_rEventSource → Record{..})
+    <$> i _rEventSource
+{-# INLINE rEventSource #-}
+
+-- | A lens for '_rEventVersion'.
+--
+-- @
+-- 'rEventVersion' ∷ Lens' 'Record' ('Maybe' 'T.Text')
+-- @
+--
+rEventVersion
+  ∷ Functor f
+  ⇒ (Maybe T.Text → f (Maybe T.Text))
+  → Record
+  → f Record
+rEventVersion i Record{..} =
+  (\_rEventVersion → Record{..})
+    <$> i _rEventVersion
+{-# INLINE rEventVersion #-}
+
+
+data ShardIteratorType
+  = ShardIteratorTrimHorizon
+  | ShardIteratorLatest
+  | ShardIteratorAtSequenceNumber
+  | ShardIteratorAfterSequenceNumber
+  deriving (Eq, Ord, Enum, Read, Show, Typeable)
+
+shardIteratorTypeToText
+  ∷ IsString s
+  ⇒ ShardIteratorType
+  → s
+shardIteratorTypeToText = \case
+  ShardIteratorTrimHorizon → "TRIM_HORIZON"
+  ShardIteratorLatest → "LATEST"
+  ShardIteratorAtSequenceNumber → "AT_SEQUENCE_NUMBER"
+  ShardIteratorAfterSequenceNumber → "AFTER_SEQUENCE_NUMBER"
+
+instance ToJSON ShardIteratorType where
+  toJSON = shardIteratorTypeToText
+
+instance FromJSON ShardIteratorType where
+  parseJSON =
+    parseEnum "ShardIteratorType" shardIteratorTypeToText
+      [ ShardIteratorTrimHorizon
+      , ShardIteratorLatest
+      , ShardIteratorAtSequenceNumber
+      , ShardIteratorAfterSequenceNumber
+      ]
+
+-- | A prism for 'ShardIteratorTrimHorizon'.
+--
+-- @
+-- '_ShardIteratorTrimHorizon' ∷ Prism' 'ShardIteratorType' '()'
+-- @
+_ShardIteratorTrimHorizon
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p ShardIteratorType (f ShardIteratorType)
+_ShardIteratorTrimHorizon =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        ShardIteratorTrimHorizon → Right ()
+        e → Left e
+      fro = either pure (const $ pure ShardIteratorTrimHorizon)
+{-# INLINE _ShardIteratorTrimHorizon #-}
+
+-- | A prism for 'ShardIteratorLatest'.
+--
+-- @
+-- '_ShardIteratorLatest' ∷ Prism' 'ShardIteratorType' '()'
+-- @
+_ShardIteratorLatest
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p ShardIteratorType (f ShardIteratorType)
+_ShardIteratorLatest =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        ShardIteratorLatest → Right ()
+        e → Left e
+      fro = either pure (const $ pure ShardIteratorLatest)
+{-# INLINE _ShardIteratorLatest #-}
+
+-- | A prism for 'ShardIteratorAtSequenceNumber'.
+--
+-- @
+-- '_ShardIteratorAtSequenceNumber' ∷ Prism' 'ShardIteratorType' '()'
+-- @
+_ShardIteratorAtSequenceNumber
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p ShardIteratorType (f ShardIteratorType)
+_ShardIteratorAtSequenceNumber =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        ShardIteratorAtSequenceNumber → Right ()
+        e → Left e
+      fro = either pure (const $ pure ShardIteratorAtSequenceNumber)
+{-# INLINE _ShardIteratorAtSequenceNumber #-}
+
+-- | A prism for 'ShardIteratorAfterSequenceNumber'.
+--
+-- @
+-- '_ShardIteratorAfterSequenceNumber' ∷ Prism' 'ShardIteratorType' '()'
+-- @
+_ShardIteratorAfterSequenceNumber
+  ∷ ( Choice p
+    , Applicative f
+    )
+  ⇒ p () (f ())
+  → p ShardIteratorType (f ShardIteratorType)
+_ShardIteratorAfterSequenceNumber =
+  dimap to fro ∘ right'
+    where
+      to = \case
+        ShardIteratorAfterSequenceNumber → Right ()
+        e → Left e
+      fro = either pure (const $ pure ShardIteratorAfterSequenceNumber)
+{-# INLINE _ShardIteratorAfterSequenceNumber #-}
+
+
+-- | The position in the shard from which to start reading stream records
+-- sequentially. A shard iterator specifies this position using the sequence
+-- number of a stream record in a shard.
+--
+-- Length constraints: @1 ≤ n ≤ 2048@.
+--
+newtype ShardIterator
+  = ShardIterator
+  { _sitText ∷ T.Text
+  } deriving (Eq, Ord, Typeable, Show, Read)
+
+instance ToJSON ShardIterator where
+  toJSON = toJSON ∘ _sitText
+
+instance FromJSON ShardIterator where
+  parseJSON =
+    withText "ShardIterator" $
+      pure ∘ ShardIterator
