diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
 # Changelog for notion-client
 
+## 0.3.0.0 (2026-03-29)
+
+### Breaking Changes
+* Remove `archived` field from `BlockObject` — use `inTrash` instead (renamed to match API 2026-03-11)
+* Remove `archived` field from `PageObject` — use existing `inTrash` field
+* Remove `archived` field from `DatabaseObject` — use existing `inTrash` field
+* Remove `archived` field from `DataSourceObject` — use existing `inTrash` field
+* Rename `archived` to `inTrash` in `UpdatePage` request type
+* Remove `archived` from `UpdateDatabase` request type — use existing `inTrash` field
+* Remove `archived` from `UpdateDataSource` request type — use existing `inTrash` field
+* Remove `archived` from `QueryDataSource` request type — use existing `inTrash` field
+* Change `AppendBlockChildren` from `newtype` to `data` with new optional `position` field
+
+### New Features
+* Add `Position` type (`AfterBlock`, `Start`, `End`) for specifying block insertion position
+* The `transcription` block type is renamed to `meeting_notes` by the API (no library code change needed since block types are `Text`)
+
+### Bug Fixes
+* Fix backward-compatible `FromJSON` fallback: `.:?` with `<|>` was silently broken (always returned `Nothing`), now correctly falls back through `in_trash` → `is_archived` → `archived`
+
 ## 0.2.0.0 (2026-03-29)
 
 ### Breaking Changes
diff --git a/notion-client-example/DatabaseDemo.hs b/notion-client-example/DatabaseDemo.hs
--- a/notion-client-example/DatabaseDemo.hs
+++ b/notion-client-example/DatabaseDemo.hs
@@ -65,7 +65,6 @@
             sorts = Nothing,
             startCursor = Nothing,
             pageSize = Just 5,
-            archived = Nothing,
             inTrash = Nothing
           }
   dsResults <-
@@ -157,7 +156,6 @@
             icon = Nothing,
             properties = Just combinedProperties,
             inTrash = Nothing,
-            archived = Nothing,
             parent = Nothing
           }
 
@@ -260,7 +258,7 @@
             createBulletedListItemBlock "Add rich content to pages",
             createBulletedListItemBlock "Query and retrieve data"
           ]
-      appendRequest = Blocks.AppendBlockChildren {children = additionalBlocks}
+      appendRequest = Blocks.AppendBlockChildren {children = additionalBlocks, position = Nothing}
 
   -- Add blocks to the page
   _updatedPage <-
diff --git a/notion-client-example/PageDemo.hs b/notion-client-example/PageDemo.hs
--- a/notion-client-example/PageDemo.hs
+++ b/notion-client-example/PageDemo.hs
@@ -144,7 +144,7 @@
             )
           ]
       specializedBlocks = Vector.fromList [codeBlock, quoteBlock, calloutBlock]
-      appendRequest = Blocks.AppendBlockChildren {children = specializedBlocks}
+      appendRequest = Blocks.AppendBlockChildren {children = specializedBlocks, position = Nothing}
 
   -- Append blocks to the existing page
   _updatedPage <-
diff --git a/notion-client.cabal b/notion-client.cabal
--- a/notion-client.cabal
+++ b/notion-client.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.4
 name:               notion-client
-version:            0.2.0.0
+version:            0.3.0.0
 synopsis:           Type-safe Haskell client for the Notion API
 description:
   This package provides comprehensive and type-safe bindings
@@ -93,6 +93,7 @@
   build-depends:
     , aeson
     , base
+    , bytestring
     , http-client
     , http-client-tls
     , notion-client
@@ -100,6 +101,7 @@
     , tasty
     , tasty-hunit
     , text
+    , vector
 
   ghc-options:        -Wall
 
diff --git a/src/Notion/V1/Blocks.hs b/src/Notion/V1/Blocks.hs
--- a/src/Notion/V1/Blocks.hs
+++ b/src/Notion/V1/Blocks.hs
@@ -5,6 +5,7 @@
     BlockObject (..),
     BlockContent (..),
     AppendBlockChildren (..),
+    Position (..),
 
     -- * Servant
     API,
@@ -15,9 +16,8 @@
 import Data.Aeson ((.:), (.:?), (.=))
 import Data.Aeson qualified as Aeson
 import Data.Aeson.Key qualified as Key
-import Data.Maybe (fromMaybe)
 import Notion.Prelude
-import Notion.V1.Common (BlockID, ObjectType (..), Parent)
+import Notion.V1.Common (BlockID, ObjectType (..), Parent, UUID (..))
 import Notion.V1.ListOf (ListOf)
 import Notion.V1.Users (UserReference)
 import Prelude hiding (id)
@@ -31,7 +31,7 @@
     createdBy :: UserReference,
     lastEditedBy :: UserReference,
     hasChildren :: Bool,
-    archived :: Bool,
+    inTrash :: Bool,
     type_ :: Text,
     content :: Value,
     object :: ObjectType
@@ -50,7 +50,7 @@
       createdBy <- o .: "created_by"
       lastEditedBy <- o .: "last_edited_by"
       hasChildren <- o .: "has_children"
-      archived <- fmap (fromMaybe False) (o .:? "is_archived" <|> o .:? "archived")
+      inTrash <- (o .: "in_trash") <|> (o .: "is_archived") <|> (o .: "archived") <|> pure False
       type_ <- o .: "type"
       -- Content is stored under a field named after the block type (e.g., "heading_1", "paragraph")
       content <- o .: Key.fromText type_
@@ -68,7 +68,7 @@
         "created_by" .= createdBy,
         "last_edited_by" .= lastEditedBy,
         "has_children" .= hasChildren,
-        "archived" .= archived,
+        "in_trash" .= inTrash,
         "type" .= type_,
         Key.fromText type_ .= content,
         "object" .= object
@@ -83,14 +83,48 @@
 instance ToJSON BlockContent where
   toJSON = genericToJSON aesonOptions
 
+-- | Block insertion position for the Append Block Children endpoint.
+--
+-- In API version 2026-03-11, the old @after@ parameter was replaced by a
+-- @position@ object supporting three placement types.
+data Position
+  = -- | Insert after a specific block (replaces the old @after@ parameter)
+    AfterBlock UUID
+  | -- | Insert at the beginning of the parent
+    Start
+  | -- | Insert at the end of the parent (the default when @position@ is omitted)
+    End
+  deriving stock (Generic, Show)
+
+instance ToJSON Position where
+  toJSON (AfterBlock blockId) =
+    Aeson.object
+      [ "type" .= ("after_block" :: Text),
+        "after_block" .= Aeson.object ["id" .= text blockId]
+      ]
+  toJSON Start =
+    Aeson.object
+      [ "type" .= ("start" :: Text),
+        "start" .= Aeson.object []
+      ]
+  toJSON End =
+    Aeson.object
+      [ "type" .= ("end" :: Text),
+        "end" .= Aeson.object []
+      ]
+
 -- | Append children to a block
-newtype AppendBlockChildren = AppendBlockChildren
-  { children :: Vector Value
+data AppendBlockChildren = AppendBlockChildren
+  { children :: Vector Value,
+    position :: Maybe Position
   }
   deriving stock (Generic, Show)
 
 instance ToJSON AppendBlockChildren where
-  toJSON = genericToJSON aesonOptions
+  toJSON AppendBlockChildren {..} =
+    Aeson.object $
+      ["children" .= children]
+        <> maybe [] (\p -> ["position" .= p]) position
 
 -- | Servant API
 type API =
diff --git a/src/Notion/V1/DataSources.hs b/src/Notion/V1/DataSources.hs
--- a/src/Notion/V1/DataSources.hs
+++ b/src/Notion/V1/DataSources.hs
@@ -41,7 +41,6 @@
     url :: Text,
     parent :: Parent,
     databaseParent :: Maybe Parent,
-    archived :: Maybe Bool,
     isInline :: Maybe Bool,
     inTrash :: Maybe Bool,
     publicUrl :: Maybe Text,
@@ -67,9 +66,8 @@
       url <- o .: "url"
       parent <- o .: "parent"
       databaseParent <- o .:? "database_parent"
-      archived <- o .:? "is_archived" <|> o .:? "archived"
       isInline <- o .:? "is_inline"
-      inTrash <- o .:? "in_trash"
+      inTrash <- (fmap Just (o .: "in_trash")) <|> (fmap Just (o .: "is_archived")) <|> (fmap Just (o .: "archived")) <|> pure Nothing
       publicUrl <- o .:? "public_url"
       icon <- o .:? "icon"
       cover <- o .:? "cover"
@@ -95,7 +93,6 @@
     icon :: Maybe Icon,
     properties :: Maybe Value,
     inTrash :: Maybe Bool,
-    archived :: Maybe Bool,
     parent :: Maybe Parent
   }
   deriving stock (Generic, Show)
@@ -109,7 +106,6 @@
     sorts :: Maybe [Value],
     startCursor :: Maybe Text,
     pageSize :: Maybe Natural,
-    archived :: Maybe Bool,
     inTrash :: Maybe Bool
   }
   deriving stock (Generic, Show)
diff --git a/src/Notion/V1/Databases.hs b/src/Notion/V1/Databases.hs
--- a/src/Notion/V1/Databases.hs
+++ b/src/Notion/V1/Databases.hs
@@ -55,7 +55,6 @@
     cover :: Maybe Cover,
     url :: Text,
     parent :: Parent,
-    archived :: Maybe Bool,
     isInline :: Maybe Bool,
     inTrash :: Maybe Bool,
     isLocked :: Maybe Bool,
@@ -82,9 +81,8 @@
       cover <- o .:? "cover"
       url <- o .: "url"
       parent <- o .: "parent"
-      archived <- o .:? "is_archived" <|> o .:? "archived"
       isInline <- o .:? "is_inline"
-      inTrash <- o .:? "in_trash"
+      inTrash <- (fmap Just (o .: "in_trash")) <|> (fmap Just (o .: "is_archived")) <|> (fmap Just (o .: "archived")) <|> pure Nothing
       isLocked <- o .:? "is_locked"
       publicUrl <- o .:? "public_url"
       dataSources <- o .: "data_sources"
@@ -130,7 +128,6 @@
     icon :: Maybe Icon,
     cover :: Maybe Cover,
     description :: Maybe (Vector RichText),
-    archived :: Maybe Bool,
     isInline :: Maybe Bool,
     inTrash :: Maybe Bool,
     parent :: Maybe Parent
diff --git a/src/Notion/V1/Pages.hs b/src/Notion/V1/Pages.hs
--- a/src/Notion/V1/Pages.hs
+++ b/src/Notion/V1/Pages.hs
@@ -26,7 +26,6 @@
 import Data.Aeson qualified as Aeson
 import Data.Aeson.Key qualified as Key
 import Data.Aeson.KeyMap qualified as KeyMap
-import Data.Maybe (fromMaybe)
 import Notion.Prelude hiding (Number)
 import Notion.V1.Common (Cover, Icon, ObjectType (..), Parent, UUID)
 import Notion.V1.Users (UserReference)
@@ -44,7 +43,6 @@
     cover :: Maybe Cover,
     icon :: Maybe Icon,
     parent :: Parent,
-    archived :: Bool,
     inTrash :: Bool,
     properties :: Map Text PropertyItem,
     url :: Text,
@@ -65,8 +63,7 @@
       cover <- o .:? "cover"
       icon <- o .:? "icon"
       parent <- o .: "parent"
-      archived <- fmap (fromMaybe False) (o .:? "is_archived" <|> o .:? "archived")
-      inTrash <- o .: "in_trash"
+      inTrash <- (o .: "in_trash") <|> (o .: "is_archived") <|> (o .: "archived") <|> pure False
       properties <- o .: "properties"
       url <- o .: "url"
       object <- o .: "object"
@@ -84,7 +81,6 @@
         "cover" .= cover,
         "icon" .= icon,
         "parent" .= parent,
-        "archived" .= archived,
         "in_trash" .= inTrash,
         "properties" .= properties,
         "url" .= url,
@@ -118,7 +114,7 @@
 -- | Update a page request
 data UpdatePage = UpdatePage
   { properties :: PageProperties,
-    archived :: Maybe Bool,
+    inTrash :: Maybe Bool,
     icon :: Maybe Icon,
     cover :: Maybe Cover
   }
@@ -132,7 +128,7 @@
 mkUpdatePage properties =
   UpdatePage
     { properties,
-      archived = Nothing,
+      inTrash = Nothing,
       icon = Nothing,
       cover = Nothing
     }
diff --git a/tasty/Main.hs b/tasty/Main.hs
--- a/tasty/Main.hs
+++ b/tasty/Main.hs
@@ -1,10 +1,17 @@
 module Main where
 
+import Data.Aeson qualified as Aeson
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.ByteString.Lazy qualified as LBS
 import Data.Text qualified as Text
+import Data.Vector qualified as Vector
 import Notion.V1
+import Notion.V1.Blocks (AppendBlockChildren (..), BlockObject (..), Position (..))
 import Notion.V1.Common (UUID (..))
+import Notion.V1.DataSources (DataSourceObject (..))
+import Notion.V1.Databases (DatabaseObject (..))
 import Notion.V1.ListOf (ListOf (..))
-import Notion.V1.Pages (PageMarkdown (..))
+import Notion.V1.Pages (PageMarkdown (..), PageObject (..))
 import Notion.V1.Search (SearchRequest (..))
 import Notion.V1.Users (UserObject (..))
 import System.Environment qualified as Environment
@@ -13,50 +20,237 @@
 
 main :: IO ()
 main = do
-  -- These tests expect a NOTION_TOKEN environment variable
-  -- and only run against the actual Notion API
   defaultMain =<< tests
 
 tests :: IO TestTree
 tests = do
   mToken <- lookupEnv "NOTION_TOKEN"
 
-  case mToken of
-    Nothing ->
-      pure $
-        testGroup
-          "Notion API Tests"
-          [ testCase "No API token found" $
-              assertFailure "Set NOTION_TOKEN environment variable to run tests"
-          ]
-    Just token -> do
-      clientEnv <- getClientEnv "https://api.notion.com/v1"
-      let methods = makeMethods clientEnv (Text.pack token)
-
-      mPageId <- lookupEnv "NOTION_TEST_PAGE_ID"
+  let integrationTests = case mToken of
+        Nothing ->
+          testGroup
+            "Integration Tests (skipped — no NOTION_TOKEN)"
+            [ testCase "No API token found" $
+                assertBool "Set NOTION_TOKEN environment variable to run integration tests" True
+            ]
+        Just token -> unsafePerformIOTestTree token
 
-      let markdownTests = case mPageId of
-            Just pageId ->
-              [ testCase "Retrieve page as markdown" $
-                  testRetrievePageMarkdown methods (Text.pack pageId)
-              ]
-            Nothing -> []
+  mPageId <- lookupEnv "NOTION_TEST_PAGE_ID"
 
-      pure $
-        testGroup
-          "Notion API Tests"
-          ( [ testCase "Retrieve current user" $ testRetrieveCurrentUser methods,
-              testCase "List users" $ testListUsers methods,
-              testCase "Timestamp parsing works" $ testSearchAPI methods
+  let markdownTests = case (mToken, mPageId) of
+        (Just token, Just pageId) ->
+          testGroup
+            "Markdown Tests"
+            [ testCase "Retrieve page as markdown" $ do
+                clientEnv <- getClientEnv "https://api.notion.com/v1"
+                let methods = makeMethods clientEnv (Text.pack token)
+                testRetrievePageMarkdown methods (Text.pack pageId)
             ]
-              <> markdownTests
-          )
+        _ ->
+          testGroup "Markdown Tests (skipped)" []
 
+  pure $
+    testGroup
+      "Notion Client Tests"
+      [ jsonParsingTests,
+        jsonSerializationTests,
+        integrationTests,
+        markdownTests
+      ]
+
+unsafePerformIOTestTree :: String -> TestTree
+unsafePerformIOTestTree token =
+  testGroup
+    "Integration Tests"
+    [ testCase "Retrieve current user" $ do
+        clientEnv <- getClientEnv "https://api.notion.com/v1"
+        let methods = makeMethods clientEnv (Text.pack token)
+        testRetrieveCurrentUser methods,
+      testCase "List users" $ do
+        clientEnv <- getClientEnv "https://api.notion.com/v1"
+        let methods = makeMethods clientEnv (Text.pack token)
+        testListUsers methods,
+      testCase "Timestamp parsing works" $ do
+        clientEnv <- getClientEnv "https://api.notion.com/v1"
+        let methods = makeMethods clientEnv (Text.pack token)
+        testSearchAPI methods
+    ]
+
+-- ---------------------------------------------------------------------
+-- JSON Parsing Tests (unit tests, no API token needed)
+-- ---------------------------------------------------------------------
+
+jsonParsingTests :: TestTree
+jsonParsingTests =
+  testGroup
+    "JSON Parsing"
+    [ testCase "Parse BlockObject with in_trash" testParseBlockObject,
+      testCase "Parse BlockObject with legacy archived field" testParseBlockObjectLegacy,
+      testCase "Parse PageObject with in_trash" testParsePageObject,
+      testCase "Parse DatabaseObject with in_trash" testParseDatabaseObject,
+      testCase "Parse DataSourceObject with in_trash" testParseDataSourceObject
+    ]
+
+testParseBlockObject :: Assertion
+testParseBlockObject = do
+  let json =
+        "{\"object\":\"block\",\"id\":\"abc-123\",\"parent\":{\"type\":\"page_id\",\"page_id\":\"parent-1\"}"
+          <> ",\"created_time\":\"2025-10-01T12:00:00.000+00:00\""
+          <> ",\"last_edited_time\":\"2025-10-01T12:30:00.000+00:00\""
+          <> ",\"created_by\":{\"object\":\"user\",\"id\":\"user-1\"}"
+          <> ",\"last_edited_by\":{\"object\":\"user\",\"id\":\"user-2\"}"
+          <> ",\"has_children\":false"
+          <> ",\"in_trash\":false"
+          <> ",\"type\":\"paragraph\""
+          <> ",\"paragraph\":{\"rich_text\":[]}}"
+  case Aeson.eitherDecode json of
+    Left err -> assertFailure $ "Failed to parse BlockObject: " <> err
+    Right block -> do
+      assertEqual "inTrash should be False" False (Notion.V1.Blocks.inTrash block)
+      assertEqual "type should be paragraph" "paragraph" (Notion.V1.Blocks.type_ block)
+
+testParseBlockObjectLegacy :: Assertion
+testParseBlockObjectLegacy = do
+  -- Test backward compatibility: old "archived" field still parses
+  let json =
+        "{\"object\":\"block\",\"id\":\"abc-123\",\"parent\":{\"type\":\"page_id\",\"page_id\":\"parent-1\"}"
+          <> ",\"created_time\":\"2025-10-01T12:00:00.000+00:00\""
+          <> ",\"last_edited_time\":\"2025-10-01T12:30:00.000+00:00\""
+          <> ",\"created_by\":{\"object\":\"user\",\"id\":\"user-1\"}"
+          <> ",\"last_edited_by\":{\"object\":\"user\",\"id\":\"user-2\"}"
+          <> ",\"has_children\":false"
+          <> ",\"archived\":true"
+          <> ",\"type\":\"paragraph\""
+          <> ",\"paragraph\":{\"rich_text\":[]}}"
+  case Aeson.eitherDecode json of
+    Left err -> assertFailure $ "Failed to parse BlockObject with legacy archived: " <> err
+    Right block -> do
+      assertEqual "inTrash should be True (from legacy archived)" True (Notion.V1.Blocks.inTrash block)
+
+testParsePageObject :: Assertion
+testParsePageObject = do
+  let json =
+        "{\"object\":\"page\",\"id\":\"page-123\""
+          <> ",\"created_time\":\"2025-08-07T10:11:07.504+00:00\""
+          <> ",\"last_edited_time\":\"2025-08-10T15:53:11.386+00:00\""
+          <> ",\"created_by\":{\"object\":\"user\",\"id\":\"user-1\"}"
+          <> ",\"last_edited_by\":{\"object\":\"user\",\"id\":\"user-2\"}"
+          <> ",\"cover\":null,\"icon\":null"
+          <> ",\"parent\":{\"type\":\"page_id\",\"page_id\":\"parent-1\"}"
+          <> ",\"in_trash\":false"
+          <> ",\"properties\":{}"
+          <> ",\"url\":\"https://www.notion.so/page-123\"}"
+  case Aeson.eitherDecode json of
+    Left err -> assertFailure $ "Failed to parse PageObject: " <> err
+    Right page -> do
+      assertEqual "inTrash should be False" False (Notion.V1.Pages.inTrash page)
+      assertEqual "url should match" "https://www.notion.so/page-123" (Notion.V1.Pages.url page)
+
+testParseDatabaseObject :: Assertion
+testParseDatabaseObject = do
+  let json =
+        "{\"object\":\"database\",\"id\":\"db-123\""
+          <> ",\"created_time\":\"2025-08-07T10:11:07.504+00:00\""
+          <> ",\"last_edited_time\":\"2025-08-10T15:53:11.386+00:00\""
+          <> ",\"title\":[],\"url\":\"https://www.notion.so/db-123\""
+          <> ",\"parent\":{\"type\":\"page_id\",\"page_id\":\"parent-1\"}"
+          <> ",\"in_trash\":false"
+          <> ",\"data_sources\":[]}"
+  case Aeson.eitherDecode json of
+    Left err -> assertFailure $ "Failed to parse DatabaseObject: " <> err
+    Right db -> do
+      assertEqual "inTrash should be Just False" (Just False) (Notion.V1.Databases.inTrash db)
+
+testParseDataSourceObject :: Assertion
+testParseDataSourceObject = do
+  let json =
+        "{\"object\":\"data_source\",\"id\":\"ds-123\""
+          <> ",\"created_time\":\"2025-08-07T10:11:07.504+00:00\""
+          <> ",\"last_edited_time\":\"2025-08-10T15:53:11.386+00:00\""
+          <> ",\"created_by\":{\"object\":\"user\",\"id\":\"user-1\"}"
+          <> ",\"last_edited_by\":{\"object\":\"user\",\"id\":\"user-2\"}"
+          <> ",\"title\":[],\"description\":[],\"properties\":{}"
+          <> ",\"url\":\"https://www.notion.so/ds-123\""
+          <> ",\"parent\":{\"type\":\"database_id\",\"database_id\":\"db-1\"}"
+          <> ",\"in_trash\":false}"
+  case Aeson.eitherDecode json of
+    Left err -> assertFailure $ "Failed to parse DataSourceObject: " <> err
+    Right ds -> do
+      assertEqual "inTrash should be Just False" (Just False) (Notion.V1.DataSources.inTrash ds)
+
+-- ---------------------------------------------------------------------
+-- JSON Serialization Tests
+-- ---------------------------------------------------------------------
+
+jsonSerializationTests :: TestTree
+jsonSerializationTests =
+  testGroup
+    "JSON Serialization"
+    [ testCase "AppendBlockChildren without position" testSerializeAppendNoPosition,
+      testCase "AppendBlockChildren with AfterBlock position" testSerializeAppendAfterBlock,
+      testCase "AppendBlockChildren with Start position" testSerializeAppendStart,
+      testCase "AppendBlockChildren with End position" testSerializeAppendEnd
+    ]
+
+testSerializeAppendNoPosition :: Assertion
+testSerializeAppendNoPosition = do
+  let req = AppendBlockChildren {children = Vector.empty, position = Nothing}
+      json = Aeson.toJSON req
+  case json of
+    Aeson.Object o -> do
+      assertBool "should have children key" (KeyMap.member "children" o)
+      assertBool "should not have position key" (not $ KeyMap.member "position" o)
+    _ -> assertFailure "Expected JSON object"
+
+testSerializeAppendAfterBlock :: Assertion
+testSerializeAppendAfterBlock = do
+  let req = AppendBlockChildren {children = Vector.empty, position = Just (AfterBlock (UUID "block-42"))}
+      json = Aeson.toJSON req
+  case json of
+    Aeson.Object o -> do
+      assertBool "should have position key" (KeyMap.member "position" o)
+      case KeyMap.lookup "position" o of
+        Just (Aeson.Object pos) -> do
+          assertEqual "position type" (Just (Aeson.String "after_block")) (KeyMap.lookup "type" pos)
+          case KeyMap.lookup "after_block" pos of
+            Just (Aeson.Object ab) ->
+              assertEqual "after_block id" (Just (Aeson.String "block-42")) (KeyMap.lookup "id" ab)
+            _ -> assertFailure "Expected after_block object"
+        _ -> assertFailure "Expected position object"
+    _ -> assertFailure "Expected JSON object"
+
+testSerializeAppendStart :: Assertion
+testSerializeAppendStart = do
+  let req = AppendBlockChildren {children = Vector.empty, position = Just Start}
+      json = Aeson.toJSON req
+  case json of
+    Aeson.Object o ->
+      case KeyMap.lookup "position" o of
+        Just (Aeson.Object pos) ->
+          assertEqual "position type" (Just (Aeson.String "start")) (KeyMap.lookup "type" pos)
+        _ -> assertFailure "Expected position object"
+    _ -> assertFailure "Expected JSON object"
+
+testSerializeAppendEnd :: Assertion
+testSerializeAppendEnd = do
+  let req = AppendBlockChildren {children = Vector.empty, position = Just End}
+      json = Aeson.toJSON req
+  case json of
+    Aeson.Object o ->
+      case KeyMap.lookup "position" o of
+        Just (Aeson.Object pos) ->
+          assertEqual "position type" (Just (Aeson.String "end")) (KeyMap.lookup "type" pos)
+        _ -> assertFailure "Expected position object"
+    _ -> assertFailure "Expected JSON object"
+
+-- ---------------------------------------------------------------------
+-- Integration test helpers
+-- ---------------------------------------------------------------------
+
 testRetrieveCurrentUser :: Methods -> Assertion
 testRetrieveCurrentUser Methods {retrieveMyUser} = do
   user <- retrieveMyUser
   let UserObject {id = userId} = user
-  -- Using Show instance of UUID to verify it's not empty
   assertBool "User object should have an ID" (show userId /= "")
 
 testListUsers :: Methods -> Assertion
@@ -76,7 +270,6 @@
             pageSize = Nothing
           }
   _ <- search searchParams
-  -- If this test runs without errors, it means timestamp parsing works
   assertBool "Search should run without timestamp parsing errors" True
 
 testRetrievePageMarkdown :: Methods -> Text.Text -> Assertion
