diff --git a/src/ZooKeeper.hs b/src/ZooKeeper.hs
--- a/src/ZooKeeper.hs
+++ b/src/ZooKeeper.hs
@@ -3,6 +3,7 @@
   , I.zooSetDebugLevel
 
   , zookeeperResInit
+  , zookeeperResInitWithWatcher
   , Res.withResource
   , Res.Resource
 
@@ -31,7 +32,9 @@
   , zooRecvTimeout
   , isUnrecoverable
 
+    -- * Internal function (should NOT be used)
   , zookeeperInit
+  , zookeeperInitWithWatcher
   , zookeeperClose
   ) where
 
@@ -44,7 +47,8 @@
 import           Foreign.C                (CInt)
 import           Foreign.ForeignPtr       (mallocForeignPtrBytes,
                                            touchForeignPtr, withForeignPtr)
-import           Foreign.Ptr              (Ptr, nullPtr, plusPtr)
+import           Foreign.Ptr              (FunPtr, Ptr, freeHaskellFunPtr,
+                                           nullPtr, plusPtr)
 import           GHC.Conc                 (newStablePtrPrimMVar)
 import           GHC.Stack                (HasCallStack, callStack)
 import           Z.Data.CBytes            (CBytes)
@@ -84,6 +88,30 @@
 zookeeperResInit host timeout mclientid flags =
   Res.initResource (zookeeperInit host timeout mclientid flags) zookeeperClose
 
+zookeeperResInitWithWatcher
+  :: CBytes
+  -- ^ host, comma separated host:port pairs, each corresponding to a zk
+  -- server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
+  -> T.WatcherFn
+  -- ^ the global watcher callback function. When notifications are
+  -- triggered this function will be invoked.
+  -> CInt
+  -- ^ timeout
+  -> Maybe T.ClientID
+  -- ^ The id of a previously established session that this client will be
+  -- reconnecting to. Pass 'Nothing' if not reconnecting to a previous
+  -- session. Clients can access the session id of an established, valid,
+  -- connection by calling 'zooGetClientID'. If the session corresponding to
+  -- the specified clientid has expired, or if the clientid is invalid for
+  -- any reason, the returned 'T.ZHandle' will be invalid -- the 'T.ZHandle'
+  -- state will indicate the reason for failure (typically 'T.ZooExpiredSession').
+  -> CInt
+  -- ^ flags, reserved for future use. Should be set to zero.
+  -> Res.Resource T.ZHandle
+zookeeperResInitWithWatcher host fn timeout mclientid flags = fst <$>
+  Res.initResource (zookeeperInitWithWatcher host fn timeout mclientid flags)
+                   (\(zh', fn')-> freeHaskellFunPtr fn' >> zookeeperClose zh')
+
 -- | Create a node.
 --
 -- This method will create a node in ZooKeeper. A node can only be created if
@@ -684,9 +712,36 @@
       I.ZooConnectedState -> return zhResult
       state -> E.throwIO $ E.ZINVALIDSTATE $ E.ZooExInfo (Text.toText state) callStack
 
-{-# INLINABLE zookeeperClose #-}
+zookeeperInitWithWatcher
+  :: CBytes
+  -- ^ host, comma separated host:port pairs, each corresponding to a zk
+  -- server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
+  -> T.WatcherFn
+  -- ^ the global watcher callback function. When notifications are
+  -- triggered this function will be invoked.
+  -> CInt
+  -- ^ timeout
+  -> Maybe T.ClientID
+  -- ^ The id of a previously established session that this client will be
+  -- reconnecting to. Pass 'Nothing' if not reconnecting to a previous
+  -- session. Clients can access the session id of an established, valid,
+  -- connection by calling 'zooGetClientID'. If the session corresponding to
+  -- the specified clientid has expired, or if the clientid is invalid for
+  -- any reason, the returned 'T.ZHandle' will be invalid -- the 'T.ZHandle'
+  -- state will indicate the reason for failure (typically 'T.ZooExpiredSession').
+  -> CInt
+  -- ^ flags, reserved for future use. Should be set to zero.
+  -> IO (T.ZHandle, FunPtr I.CWatcherFn)
+zookeeperInitWithWatcher host fn timeout mclientid flags = do
+  let clientid = fromMaybe (I.ClientID nullPtr) mclientid
+  fnPtr <- I.mkWatcherFnPtr fn
+  zh <- CBytes.withCBytes host $ \host' -> do
+    I.zookeeper_init host' fnPtr timeout clientid nullPtr flags
+  return (zh, fnPtr)
+
 zookeeperClose :: T.ZHandle -> IO ()
 zookeeperClose = void . E.throwZooErrorIfNotOK <=< I.c_zookeeper_close_safe
+{-# INLINABLE zookeeperClose #-}
 
 -- | Return the client session id, only valid if the connections
 -- is currently connected (ie. last watcher state is 'T.ZooConnectedState')
diff --git a/src/ZooKeeper/Internal/FFI.hsc b/src/ZooKeeper/Internal/FFI.hsc
--- a/src/ZooKeeper/Internal/FFI.hsc
+++ b/src/ZooKeeper/Internal/FFI.hsc
@@ -16,6 +16,7 @@
 import           GHC.Conc
 import           GHC.Stack                    (HasCallStack)
 import           Text.ParserCombinators.ReadP (readP_to_S)
+import qualified Z.Data.CBytes                as CBytes
 import           Z.Foreign
 
 import           ZooKeeper.Exception
@@ -44,6 +45,14 @@
 foreign import ccall unsafe "hs_zk.h zoo_set_debug_level"
   zooSetDebugLevel :: ZooLogLevel -> IO ()
 
+foreign import ccall "wrapper"
+  mkCWatcherFnPtr :: CWatcherFn -> IO (FunPtr CWatcherFn)
+
+mkWatcherFnPtr :: WatcherFn -> IO (FunPtr CWatcherFn)
+mkWatcherFnPtr fn = mkCWatcherFnPtr $ \zh ev st cpath _ctx -> do
+  path <- CBytes.fromCString cpath
+  fn zh (ZooEvent ev) (ZooState st) path
+
 foreign import ccall unsafe "hs_zk.h hs_zookeeper_init"
   c_hs_zookeeper_init
     :: StablePtr PrimMVar -> Int -> Ptr HsWatcherCtx
@@ -53,6 +62,16 @@
     -> CInt
     -> IO ZHandle
 
+foreign import ccall safe "zookeeper.h zookeeper_init"
+  zookeeper_init
+    :: Ptr Word8
+    -> FunPtr CWatcherFn
+    -> CInt
+    -> ClientID
+    -> Ptr a
+    -> CInt
+    -> IO ZHandle
+
 foreign import ccall safe "hs_zk.h zookeeper_close"
   c_zookeeper_close_safe :: ZHandle -> IO CInt
 
@@ -214,6 +233,9 @@
     -> CInt
     -> MBA## Word8     -- pointer to Stat
     -> IO ()
+
+foreign import ccall safe "zookeeper.h zoo_set_watcher"
+  zoo_set_watcher :: ZHandle -> FunPtr CWatcherFn -> IO (FunPtr CWatcherFn)
 
 foreign import ccall unsafe "hs_zk.h zoo_check_op_init"
   c_zoo_check_op_init :: Ptr CZooOp -> BA## Word8 -> CInt -> IO ()
diff --git a/src/ZooKeeper/Internal/Types.hsc b/src/ZooKeeper/Internal/Types.hsc
--- a/src/ZooKeeper/Internal/Types.hsc
+++ b/src/ZooKeeper/Internal/Types.hsc
@@ -19,6 +19,9 @@
 
 -------------------------------------------------------------------------------
 
+type CWatcherFn = ZHandle -> CInt     -> CInt     -> CString -> Ptr () -> IO ()
+type WatcherFn  = ZHandle -> ZooEvent -> ZooState -> CBytes            -> IO ()
+
 newtype ZHandle = ZHandle { unZHandle :: Ptr () }
   deriving (Show, Eq)
 
diff --git a/src/ZooKeeper/Types.hs b/src/ZooKeeper/Types.hs
--- a/src/ZooKeeper/Types.hs
+++ b/src/ZooKeeper/Types.hs
@@ -1,6 +1,7 @@
 module ZooKeeper.Types
   ( I.ZHandle
   , I.ClientID
+  , I.WatcherFn
 
   , I.ZooOp
   , I.ZooOpResult (..)
diff --git a/zoovisitor.cabal b/zoovisitor.cabal
--- a/zoovisitor.cabal
+++ b/zoovisitor.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               zoovisitor
-version:            0.1.7.0
+version:            0.1.8.0
 synopsis:
   A haskell binding to Apache Zookeeper C library(mt) using Haskell Z project.
 
