diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
 # Revision history for hermes-json
 
+## 0.8.1.0 -- 2026-08-30
+
+### Changed
+* Update simdjson to 4.6.9
+
+### Added
+* Add `objectFold` for folding over object fields with a dependent state -
+  thanks to @mpscholten!
+* Add `withRawJsonByteString` for access to the raw JSON of the current value -
+  thanks to @mpscholten!
+
 ## 0.8.0.0 -- 2026-03-25
 
 Maintenance release
diff --git a/cbits/lib.cpp b/cbits/lib.cpp
--- a/cbits/lib.cpp
+++ b/cbits/lib.cpp
@@ -202,6 +202,20 @@
     len = buf.length();
   }
 
+  error_code get_raw_json(
+      ondemand::value &val,
+      const char **out,
+      size_t &len) {
+    std::string_view buf;
+    auto error = val.raw_json().get(buf);
+    if (error) {
+      return error;
+    }
+    *out = buf.data();
+    len = buf.length();
+    return SUCCESS;
+  }
+
   error_code is_null(ondemand::value &val, bool &out) {
     return val.is_null().get(out);
   }
diff --git a/hermes-json.cabal b/hermes-json.cabal
--- a/hermes-json.cabal
+++ b/hermes-json.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               hermes-json
-version:            0.8.0.0
+version:            0.8.1.0
 category:           Text, Web, JSON, FFI
 synopsis:           Fast JSON decoding via simdjson C++ bindings
 description:
diff --git a/simdjson/singleheader/simdjson.cpp b/simdjson/singleheader/simdjson.cpp
# file too large to diff: simdjson/singleheader/simdjson.cpp
diff --git a/simdjson/singleheader/simdjson.h b/simdjson/singleheader/simdjson.h
# file too large to diff: simdjson/singleheader/simdjson.h
diff --git a/src/Data/Hermes.hs b/src/Data/Hermes.hs
--- a/src/Data/Hermes.hs
+++ b/src/Data/Hermes.hs
@@ -51,6 +51,7 @@
   , vector
   , nullable
   , objectAsKeyValues
+  , objectFold
   , objectAsMap
   , objectAsMapExcluding
   , liftObjectDecoder
@@ -84,6 +85,7 @@
   , withVector
   -- * Raw ByteString access
   , withRawByteString
+  , withRawJsonByteString
     -- * simdjson Opaque Types
   , Array
   , ArrayIter
diff --git a/src/Data/Hermes/Decoder/Value.hs b/src/Data/Hermes/Decoder/Value.hs
--- a/src/Data/Hermes/Decoder/Value.hs
+++ b/src/Data/Hermes/Decoder/Value.hs
@@ -18,6 +18,7 @@
   , nullable
   , object
   , objectAsKeyValues
+  , objectFold
   , objectAsMap
   , objectAsMapExcluding
   , parseScientific
@@ -33,6 +34,7 @@
   , withInt
   , withObjectAsMap
   , withRawByteString
+  , withRawJsonByteString
   , withRawText
   , withScientific
   , withString
@@ -115,6 +117,16 @@
 withRawByteString f = Decoder $ \val -> getRawByteString val >>= \b -> runDecoder (f b) val
 {-# INLINE withRawByteString #-}
 
+-- | Helper to work with the complete raw JSON representation of the current
+-- value. Unlike 'withRawByteString', this consumes and returns a complete
+-- nested array or object rather than only its opening token.
+withRawJsonByteString :: (BS.ByteString -> Decoder a) -> Decoder a
+withRawJsonByteString f =
+  Decoder $ \val ->
+    getRawJsonByteString val >>= \bytes ->
+      runDecoder (f bytes) val
+{-# INLINE withRawJsonByteString #-}
+
 -- | Helper to work with the raw ByteString of the JSON token parsed from the given Value.
 withRawText :: (Text -> Decoder a) -> Decoder a
 withRawText f = Decoder $ \val -> getRawText val >>= \b -> runDecoder (f b) val
@@ -208,6 +220,17 @@
 objectAsKeyValues kf vf = withObjectIter $ iterateOverFields kf vf
 {-# INLINE objectAsKeyValues #-}
 
+-- | Fold over an object once, selecting the value decoder from the current
+-- key and accumulator. This supports dependent object codecs without
+-- materialising a key/value list or rescanning the object.
+objectFold
+  :: state
+  -> (Text -> state -> Decoder state)
+  -> Decoder state
+objectFold initial step =
+  withObjectIter $ iterateOverFieldsFold initial step
+{-# INLINE objectFold #-}
+
 -- | Parse an object into a strict `Map`.
 objectAsMap
   :: Ord k
@@ -516,6 +539,32 @@
           pure $ DList.toList acc
 {-# INLINE iterateOverFields #-}
 
+iterateOverFieldsFold
+  :: state
+  -> (Text -> state -> Decoder state)
+  -> ObjectIter
+  -> DecoderM state
+iterateOverFieldsFold initial step iterPtr =
+  withRunInIO $ \run ->
+    F.alloca $ \lenPtr ->
+      F.alloca $ \keyPtr ->
+        allocaValue $ \valPtr -> run $ go initial keyPtr lenPtr valPtr
+  where
+    go !acc keyPtr lenPtr valPtr = do
+      isOver <- fmap F.toBool . liftIO $ objectIterIsDoneImpl iterPtr
+      if not isOver
+        then do
+          err <- liftIO $ objectIterGetCurrentImpl iterPtr keyPtr lenPtr valPtr
+          handleErrorCode "" err
+          keyLength <- fmap fromIntegral . liftIO $ F.peek lenPtr
+          keyCString <- liftIO $ F.peek keyPtr
+          key <- parseTextFromCStrLen (keyCString, keyLength)
+          next <- withKey key $ runDecoder (step key acc) valPtr
+          liftIO $ objectIterMoveNextImpl iterPtr
+          go next keyPtr lenPtr valPtr
+        else pure acc
+{-# INLINE iterateOverFieldsFold #-}
+
 withUnorderedField :: Value -> Decoder a -> Object -> Text -> DecoderM a
 withUnorderedField vPtr f objPtr key =
   withRunInIO $ \run ->
@@ -618,6 +667,20 @@
         str <- F.peek strPtr
         Unsafe.unsafePackCStringLen (str, len)
 {-# INLINE getRawByteString #-}
+
+getRawJsonByteString :: Value -> DecoderM BS.ByteString
+getRawJsonByteString valPtr = do
+  (errorCode, cString, byteLength) <-
+    liftIO $
+      F.alloca $ \stringPtr ->
+        F.alloca $ \lengthPtr -> do
+          errorCode <- getRawJSONImpl valPtr stringPtr lengthPtr
+          byteLength <- fmap fromIntegral $ F.peek lengthPtr
+          cString <- F.peek stringPtr
+          pure (errorCode, cString, byteLength)
+  handleErrorCode "raw JSON" errorCode
+  liftIO $ Unsafe.unsafePackCStringLen (cString, byteLength)
+{-# INLINE getRawJsonByteString #-}
 
 getRawText :: Value -> DecoderM Text
 getRawText valPtr =
diff --git a/src/Data/Hermes/SIMDJSON/Bindings.hs b/src/Data/Hermes/SIMDJSON/Bindings.hs
--- a/src/Data/Hermes/SIMDJSON/Bindings.hs
+++ b/src/Data/Hermes/SIMDJSON/Bindings.hs
@@ -23,6 +23,7 @@
   , getObjectFromValueImpl
   , getObjectIterFromValueImpl
   , getRawJSONTokenImpl
+  , getRawJSONImpl
   , getStringImpl
   , getTypeImpl
   , intArrayImpl
@@ -145,6 +146,9 @@
 
 foreign import ccall unsafe "get_raw_json_token" getRawJSONTokenImpl
   :: Value -> Ptr CString -> Ptr CSize -> IO ()
+
+foreign import ccall unsafe "get_raw_json" getRawJSONImpl
+  :: Value -> Ptr CString -> Ptr CSize -> IO CInt
 
 foreign import ccall unsafe "get_type" getTypeImpl
   :: Value -> Ptr CInt -> IO CInt
diff --git a/tests/test.hs b/tests/test.hs
--- a/tests/test.hs
+++ b/tests/test.hs
@@ -37,7 +37,26 @@
 properties = testGroup "Properties" [rtProp, rtPropOptional, rtErrors, rtRecursiveDataType]
 
 units :: TestTree
-units = testGroup "Units" [altCases, objectFields]
+units = testGroup "Units" [altCases, objectFields, dependentObjectFold, rawJsonAccess]
+
+dependentObjectFold :: TestTree
+dependentObjectFold = testCase "dependent object fold" $
+  decodeEither
+    (objectFold (0 :: Int) $ \key total ->
+      case key of
+        "add" -> (+ total) <$> int
+        "enabled" -> (\value -> if value then total + 100 else total) <$> bool
+        _ -> total <$ withRawJsonByteString (const (pure ())))
+    "{\"add\":2,\"ignored\":{\"nested\":[1,2]},\"enabled\":true,\"add\":3}"
+    @?= Right 105
+
+rawJsonAccess :: TestTree
+rawJsonAccess = testCase "complete raw JSON access" $
+  decodeEither
+    (object $ atKey "payload" $
+      withRawJsonByteString (\bytes -> pure bytes))
+    "{\"payload\":{\"nested\":[1,true,null]}}"
+    @?= Right "{\"nested\":[1,true,null]}"
 
 rtRecursiveDataType :: TestTree
 rtRecursiveDataType = testProperty "Round Trip With Recursive Data Type" $
