diff --git a/src/Monitor/Tracing/Zipkin.hs b/src/Monitor/Tracing/Zipkin.hs
--- a/src/Monitor/Tracing/Zipkin.hs
+++ b/src/Monitor/Tracing/Zipkin.hs
@@ -62,6 +62,7 @@
 import Data.Semigroup ((<>))
 #endif
 import Data.Set (Set)
+import qualified Data.Set as Set
 import Data.String (IsString(..))
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -166,7 +167,7 @@
 --
 -- > childSpanWith (addTag "key" "value") "run" $ action
 --
--- Note that there is not difference with adding the tag after the span. So the above code is
+-- Note that there is no difference with adding the tag after the span. So the above code is
 -- equivalent to:
 --
 -- > childSpan "run" $ tag "key" "value" >> action
@@ -303,9 +304,9 @@
       else sampledWhen $ b3IsSampled b3
   in Endo $ \bldr -> bldr
     { builderTraceID = Just (b3TraceID b3)
-    , builderSpanID = Just (b3SpanID b3)
     , builderSamplingPolicy = Just policy }
 
+-- Prefix added to all user tags. This protects against collisions with internal tags.
 publicKeyPrefix :: Text
 publicKeyPrefix = "Z."
 
@@ -317,8 +318,6 @@
 kindKey :: Key
 kindKey = "z.k"
 
--- Internal keys
-
 outgoingSpan :: MonadTrace m => Text -> Endo Builder -> Name -> (Maybe B3 -> m a) -> m a
 outgoingSpan kind endo name f = childSpanWith (appEndo endo') name actn where
   endo' = insertTag kindKey kind <> endo
@@ -348,9 +347,9 @@
 producerSpanWith :: MonadTrace m => (Builder -> Builder) -> Name -> (Maybe B3 -> m a) -> m a
 producerSpanWith f = outgoingSpan "PRODUCER" (Endo f)
 
-incomingSpan :: MonadTrace m => Text -> Endo Builder -> B3 -> m a -> m a
-incomingSpan kind endo b3 actn =
-  let bldr = appEndo (importB3 b3 <> insertTag kindKey kind <> endo) $ builder ""
+incomingSpan :: MonadTrace m => Text -> B3 -> Endo Builder -> m a -> m a
+incomingSpan kind b3 endo actn =
+  let bldr = appEndo (insertTag kindKey kind <> importB3 b3 <> endo) $ builder ""
   in trace bldr actn
 
 -- | Generates a child span with @SERVER@ kind. The client's 'B3' should be provided as input,
@@ -358,14 +357,22 @@
 serverSpan :: MonadTrace m => B3 -> m a -> m a
 serverSpan = serverSpanWith id
 
--- | Generates a server span, optionally modifying the span's builder. This can be useful in
--- combination with 'addEndpoint' if the remote client does not have tracing enabled.
+-- | Generates a child span with @SERVER@ kind, optionally modifying the span's builder. This can
+-- be useful in combination with 'addEndpoint' if the remote client does not have tracing enabled.
+-- The clients's 'B3' should be provided as input. Client and server annotations go on the same
+-- span - it means that they share their span ID.
 serverSpanWith :: MonadTrace m => (Builder -> Builder) -> B3 -> m a -> m a
-serverSpanWith f = incomingSpan "SERVER" (Endo f)
+serverSpanWith f b3 =
+  let endo = Endo $ \bldr -> f $ bldr { builderSpanID = Just (b3SpanID b3) }
+  in incomingSpan "SERVER" b3 endo
 
--- | Generates a child span with @CONSUMER@ kind. The producer's 'B3' should be provided as input.
+-- | Generates a child span with @CONSUMER@ kind, optionally modifying the span's builder. The
+-- producer's 'B3' should be provided as input. The generated span will have its parent ID set to
+-- the input B3's span ID.
 consumerSpanWith :: MonadTrace m => (Builder -> Builder) -> B3 -> m a -> m a
-consumerSpanWith f = incomingSpan "CONSUMER" (Endo f)
+consumerSpanWith f b3 =
+  let endo = Endo $ \bldr -> f $ bldr { builderReferences = Set.singleton (ChildOf $ b3SpanID b3) }
+  in incomingSpan "CONSUMER" b3 endo
 
 -- | Information about a hosted service, included in spans and visible in the Zipkin UI.
 data Endpoint = Endpoint
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -14,6 +14,7 @@
 import Control.Monad.State.Strict (MonadState, StateT, evalStateT, get)
 import qualified Data.Map.Strict as Map
 import Data.Text (Text)
+import qualified Data.Set as Set
 import Test.Hspec
 import Test.Hspec.QuickCheck
 import UnliftIO
@@ -66,6 +67,14 @@
         Just b3 = ZPK.b3FromHeaderValue bs
         Just b3' = ZPK.b3FromHeaders hdrs
       b3 `shouldBe` b3'
+    it "consumerSpan should use B3 as parent reference" $ do
+      let
+        bs = "80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90"
+        Just b3 = ZPK.b3FromHeaderValue bs
+      [consumerSpan] <- collectSpans $ ZPK.consumerSpanWith id b3 $ pure ()
+      contextTraceID (spanContext consumerSpan) `shouldBe` ZPK.b3TraceID b3            -- same traceId
+      contextSpanID (spanContext consumerSpan) `shouldNotBe` ZPK.b3SpanID b3           -- different spanId
+      spanReferences consumerSpan `shouldBe` Set.singleton (ChildOf $ ZPK.b3SpanID b3) -- b3 spanId is parent
   describe "collectSpanSamples" $ do
     it "should collect spans which are still pending after the action returns" $ do
       spans <- collectSpans $ rootSpan alwaysSampled "sleep-parent" $ do
diff --git a/tracing.cabal b/tracing.cabal
--- a/tracing.cabal
+++ b/tracing.cabal
@@ -1,7 +1,7 @@
 cabal-version: 1.12
 
 name: tracing
-version: 0.0.5.2
+version: 0.0.6.0
 synopsis: Distributed tracing
 description:
   An OpenTracing-compliant, simple, and extensible distributed tracing library.
@@ -44,7 +44,7 @@
     , random >= 1.1
     , stm >= 2.5
     , text >= 1.2
-    , time >= 1.8
+    , time >= 1.8 && < 1.10
     , transformers >= 0.5
     , unliftio >= 0.2
   ghc-options: -Wall
