diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,10 +1,12 @@
 # Changelog for aeson-deriving
 
-# Revision history for servant-openapi
+### 0.1.2
 
+* Added `EmptyObject` newtype for single-constructor types to be encoded as an empty JSON object.
+
 ### 0.1.1
 
-* Added newtype for Text validated against a regex pattern [PR 2](https://github.com/fieldstrength/aeson-deriving/pull/2)
+* Added `TextWithPattern` newtype for Text validated against a regex pattern [PR 2](https://github.com/fieldstrength/aeson-deriving/pull/2)
 
 ## 0.1.0
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@
 
 data User = User
   { firstName :: Text
-  , id_       :: UserId        
+  , id_       :: UserId
   , companyId :: CompanyId
   }
   deriving stock (Generic, Show)
@@ -54,26 +54,41 @@
   deriving stock (Generic, Show)
   deriving (FromJSON, ToJSON) via
     WithConstantFieldsOut
-      '[ "version" := "1.0"
-      , "system_info" := "👍" ]
-        MyEncoding Transaction
+     '[ "version" := "1.0"
+      , "system_info" := "👍"
+      ]
+      (MyEncoding Transaction)
 ```
 
 Note: Some newtypes that modify the instances come in an inbound and outbound variant. For example `WithConstantFields` is defined as the composition of `WithConstantFieldsIn` and `WithConstantFieldsOut`.
 
+#### Constant Objects
+
+Sometimes you may need an entire object of constant fields, with no information passing to the haskell representation. This is modeled as a single-value type and can also be used with the `WithConstantFields` newtype, as long as the base type is wrapped in the `EmptyObject` newtype (because otherwise unit types do not serialize to the empty object by default).
+
+```haskell
+data Requirements = Requirements
+  deriving (Show, Eq, Generic)
+  deriving (FromJSON, ToJSON) via
+    WithConstantFields
+     '[ "api_version" := "2.0"
+      , "check_performed" := 'True
+      ]
+      (EmptyObject Requirements)
+```
+
 #### Apply arbitrary functions before encoding/decoding
 
 ##### Example: Special treatment for magic values
 
 ```haskell
-
 data Feedback = Feedback
   { comment :: Text }
   deriving stock (Generic, Show)
   deriving (FromJSON, ToJSON) via
     ModifyFieldIn "comment"
       ("booo!" ==> "boo-urns!")
-        MyEncoding Feedback
+      (MyEncoding Feedback)
 
 
 -- x ==> y  maps the value x to y and leaves others unchanged
diff --git a/aeson-deriving.cabal b/aeson-deriving.cabal
--- a/aeson-deriving.cabal
+++ b/aeson-deriving.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: afa1a88618274c13cbf349c67513735bd434bd45398e9173e13419d361544a8a
+-- hash: 05a421de32621a1d52633e1311198dbc32abe2ea1f27c1e0f003d14a579b97e5
 
 name:           aeson-deriving
-version:        0.1.1.1
+version:        0.1.1.2
 synopsis:       data types for compositional, type-directed serialization
 description:    Please see the README on GitHub at <https://github.com/fieldstrength/aeson-deriving#readme>
 category:       Serialization
@@ -36,6 +36,7 @@
       Data.Aeson.Deriving.Known
       Data.Aeson.Deriving.ModifyField
       Data.Aeson.Deriving.SingleFieldObject
+      Data.Aeson.Deriving.EmptyObject
       Data.Aeson.Deriving.Utils
       Data.Aeson.Deriving.WithConstantFields
       Data.Aeson.Deriving.Text
@@ -47,7 +48,7 @@
   default-extensions: ConstraintKinds DataKinds DeriveFunctor DeriveGeneric DerivingStrategies FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns OverloadedStrings ScopedTypeVariables TupleSections TypeApplications TypeOperators
   ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns
   build-depends:
-      aeson >=1.2 && <1.5
+      aeson >=1.2 && <1.6
     , base >=4.7 && <5
     , regex-tdfa
     , text
@@ -64,7 +65,7 @@
   default-extensions: ConstraintKinds DataKinds DeriveFunctor DeriveGeneric DerivingStrategies FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns OverloadedStrings ScopedTypeVariables TupleSections TypeApplications TypeOperators
   ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns
   build-depends:
-      aeson >=1.2 && <1.5
+      aeson >=1.2 && <1.6
     , aeson-deriving
     , base >=4.7 && <5
     , hedgehog
diff --git a/src/Data/Aeson/Deriving.hs b/src/Data/Aeson/Deriving.hs
--- a/src/Data/Aeson/Deriving.hs
+++ b/src/Data/Aeson/Deriving.hs
@@ -6,3 +6,4 @@
 import           Data.Aeson.Deriving.SingleFieldObject  as AllExports
 import           Data.Aeson.Deriving.Text               as AllExports
 import           Data.Aeson.Deriving.WithConstantFields as AllExports
+import           Data.Aeson.Deriving.EmptyObject        as AllExports
diff --git a/src/Data/Aeson/Deriving/EmptyObject.hs b/src/Data/Aeson/Deriving/EmptyObject.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Aeson/Deriving/EmptyObject.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE UndecidableInstances #-}
+
+-- | Allow unit-like types to be serialized as the empty object
+--   This can be combined with 'WithConstantFields'.
+module Data.Aeson.Deriving.EmptyObject where
+
+import Data.Aeson
+import Data.Kind (Type)
+import GHC.Generics
+
+-- | For data types with exactly one value, this data type changes the serialization to be the empty JSON object.
+--   It can be combined with 'WithConstantFields'.
+newtype EmptyObject a = EmptyObject a
+
+instance UnitLike (Rep a) => ToJSON (EmptyObject a) where
+  toJSON _ = object []
+
+instance (Generic a, UnitLike (Rep a)) => FromJSON (EmptyObject a) where
+  parseJSON = withObject "object" $ \_hashmap ->
+    pure . EmptyObject $ to gPoint
+
+
+-- | class for data types with a single constructor
+class UnitLike (f :: Type -> Type) where
+  gPoint :: f a
+
+instance UnitLike U1 where
+  gPoint = U1
+
+instance UnitLike a => UnitLike (M1 C meta a) where
+  gPoint = M1 gPoint
+
+instance UnitLike a => UnitLike (M1 D meta a) where
+  gPoint = M1 gPoint
+
+
diff --git a/src/Data/Aeson/Deriving/Internal/Generic.hs b/src/Data/Aeson/Deriving/Internal/Generic.hs
--- a/src/Data/Aeson/Deriving/Internal/Generic.hs
+++ b/src/Data/Aeson/Deriving/Internal/Generic.hs
@@ -5,8 +5,9 @@
 module Data.Aeson.Deriving.Internal.Generic where
 
 import           Data.Aeson
-import           Data.Aeson.Deriving.Known
+import           Data.Aeson.Deriving.EmptyObject        (EmptyObject(..))
 import           Data.Aeson.Deriving.Internal.RecordSum
+import           Data.Aeson.Deriving.Known
 import           Data.Aeson.Deriving.Utils
 import           Data.Aeson.Types                       (modifyFailure)
 import           Data.Char                              (isUpper, toLower, toUpper)
@@ -191,6 +192,7 @@
 type family LoopWarning (n :: Type -> Type) (a :: Type) :: Constraint where
   LoopWarning n (GenericEncoded opts a) = ()
   LoopWarning n (RecordSumEncoded tagKey tagValMod a) = ()
+  LoopWarning n (EmptyObject a) = ()
   LoopWarning n (DisableLoopWarning a) = ()
   LoopWarning n (x & f) = LoopWarning n (f x)
   LoopWarning n (f x) = LoopWarning n x
diff --git a/src/Data/Aeson/Deriving/WithConstantFields.hs b/src/Data/Aeson/Deriving/WithConstantFields.hs
--- a/src/Data/Aeson/Deriving/WithConstantFields.hs
+++ b/src/Data/Aeson/Deriving/WithConstantFields.hs
@@ -49,5 +49,7 @@
         flip (withObject "Object") valIn $ \obj -> do
           valActual <- obj .: key
           unless (valActual == valExpected) . fail $
-            "Expected constant value `" <> show valExpected <> "` but got: " <>
-            show valActual
+            "Expected constant value " <> showEscapedJson valExpected <>
+            " but got: " <> showEscapedJson valActual
+
+      showEscapedJson val = show (encode val)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -217,3 +217,37 @@
 prop_first_char_modifier_decodes_as_expected = once . property $ do
   for_ [BlackHole ..] $ \x ->
     tripping x encode decode
+
+
+data Unity = Unity
+  deriving (Show, Eq, Generic)
+  deriving (FromJSON, ToJSON) via EmptyObject Unity
+
+prop_empty_object_encodes_as_expected :: Property
+prop_empty_object_encodes_as_expected = once . property $ do
+  encode Unity === "{}"
+
+prop_empty_object_decodes_as_expected :: Property
+prop_empty_object_decodes_as_expected = once . property $ tripping Unity encode decode
+
+-- An example of how to require a particular constant object
+data Requirements = Requirements
+  deriving (Show, Eq, Generic)
+  deriving (FromJSON, ToJSON) via
+    WithConstantFields
+     '[ "api_version" := "2.0"
+      , "check_performed" := 'True
+      ]
+      (EmptyObject Requirements)
+
+prop_constant_object_encodes_as_expected :: Property
+prop_constant_object_encodes_as_expected = once . property $
+  encode Requirements === "{\"api_version\":\"2.0\",\"check_performed\":true}"
+
+prop_constant_object_decodes_as_expected :: Property
+prop_constant_object_decodes_as_expected = once . property $ tripping Requirements encode decode
+
+prop_reject_constant_object_with_incorrect_details :: Property
+prop_reject_constant_object_with_incorrect_details = once . property $
+  eitherDecode @Requirements "{\"api_version\":\"2.0\",\"check_performed\":false}"
+    === Left "Error in $: Expected constant value \"true\" but got: \"false\""
