You can build a zipper to navigate that structure like the following:
(def z (zip/zipper
(constantly true) ; a node can always have children
:nodes ; child nodes in the :nodes key, and keywords are functions
(fn [node children]
(update-in node [:nodes] #(into (or % []) children)))
document-test))
(-> z
zip/down
zip/right
zip/right
(zip/insert-child {:type "h3" :id "thing1" :content "It's a h3!"})
zip/down
zip/right
zip/right
zip/node)
You may also want to look at the parse tree format of Enlive or Instaparse for ideas about structure. Reusing their structure may give you some interoperability you don't get with rolling your own.