diff --git a/cmd/dumpRounds.go b/cmd/dumpRounds.go
index 71273d07e111c1504754e73fb31246c65a726a4b..88743972921640bc169fd7213122e07254af77b0 100644
--- a/cmd/dumpRounds.go
+++ b/cmd/dumpRounds.go
@@ -156,7 +156,7 @@ func dumpRounds(roundIDs []id.Round, user *xxdk.E2e) []rounds.Round {
 	for done := 0; done < numRequests; done++ {
 		res := <-requestCh
 		roundInfos = append(roundInfos, res)
-		fmt.Printf("request complete: %v", res)
+		jww.DEBUG.Printf("request complete: %v", res)
 	}
 	return roundInfos
 }
diff --git a/cmd/flags.go b/cmd/flags.go
index 5244b7a12b62ee1e1c7d5a718f45b2bbccfad005..70a59ce00c16fcd834dcd50987b12626c10f8504 100644
--- a/cmd/flags.go
+++ b/cmd/flags.go
@@ -148,4 +148,8 @@ const (
 	udSearchEmailFlag    = "searchemail"
 	udSearchPhoneFlag    = "searchphone"
 	udBatchAddFlag       = "batchadd"
+
+	///////////////// pickup subcommand flags //////////////////////////////
+	pickupGW = "gateway"
+	pickupID = "id"
 )
diff --git a/cmd/pickup.go b/cmd/pickup.go
new file mode 100644
index 0000000000000000000000000000000000000000..92331266e26c41cb6a67f3f6ea52ff7108f580af
--- /dev/null
+++ b/cmd/pickup.go
@@ -0,0 +1,168 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+// Package cmd initializes the CLI and config parsers as well as the logger.
+package cmd
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/pkg/errors"
+
+	"github.com/spf13/cobra"
+	jww "github.com/spf13/jwalterweatherman"
+	"github.com/spf13/viper"
+	"gitlab.com/elixxir/client/cmix/pickup"
+	pb "gitlab.com/elixxir/comms/mixmessages"
+	"gitlab.com/elixxir/primitives/states"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/crypto/randomness"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"gitlab.com/xx_network/primitives/ndf"
+)
+
+// pickupCmd allows the user to view network information about a specific
+// round on the network.
+var pickupCmd = &cobra.Command{
+	Use:   "pickup",
+	Short: "Download the bloomfilter and messages for a round",
+	Args:  cobra.MinimumNArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		roundIDs := parseRoundIDs(args)
+
+		cmixParams, e2eParams := initParams()
+		authCbs := makeAuthCallbacks(
+			viper.GetBool(unsafeChannelCreationFlag), e2eParams)
+		user := initE2e(cmixParams, e2eParams, authCbs)
+		err := user.StartNetworkFollower(5 * time.Second)
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+
+		connected := make(chan bool, 10)
+		user.GetCmix().AddHealthCallback(
+			func(isconnected bool) {
+				connected <- isconnected
+			})
+		waitUntilConnected(connected)
+
+		ndf := user.GetStorage().GetNDF()
+
+		gwID := getGatewayID(ndf)
+		clientID := parseRecipient(viper.GetString(pickupID))
+
+		// First we get round info, then we use the timestamps to
+		// calculate the right ephID and retrieve the right bloom filter
+		roundInfos := dumpRounds(roundIDs, user)
+		for i := range roundInfos {
+			ri := roundInfos[i]
+			ephIDs := getEphID(clientID, uint(ri.AddressSpaceSize),
+				ri.Timestamps[states.QUEUED])
+
+			for j := range ephIDs {
+				ephID := ephIDs[j]
+				fmt.Printf("Getting messages for %s, %d\n",
+					ri.ID, ephID.Id.Int64())
+				msgRsp, err := getMessagesFromRound(gwID, ri.ID,
+					ephID.Id,
+					user.GetComms())
+				if err != nil {
+					fmt.Printf("\n\nround pickup: %+v\n\n",
+						err)
+				}
+				fmt.Printf("=====ROUNDPICKUP=====\n\n%+v\n\n\n", msgRsp)
+				fmt.Printf("%d messages for user %d", len(msgRsp.Messages), ephIDs)
+				for k := range msgRsp.Messages {
+					fmt.Printf("%v\n", msgRsp.Messages[k].PayloadA)
+				}
+			}
+		}
+	},
+}
+
+func init() {
+	pickupCmd.Flags().StringP(pickupGW, "g", "",
+		"gateway (base64 address string) to download from")
+	bindFlagHelper(pickupGW, pickupCmd)
+
+	pickupCmd.Flags().StringP(pickupID, "i", "",
+		"id to check")
+	bindFlagHelper(pickupID, pickupCmd)
+
+	rootCmd.AddCommand(pickupCmd)
+}
+
+func getEphID(id *id.ID, addrSize uint,
+	roundStart time.Time) []ephemeral.ProtoIdentity {
+
+	fmt.Printf("Getting EphIDs for %s", roundStart)
+
+	ephIDs, err := ephemeral.GetIdsByRange(id,
+		addrSize,
+		roundStart,
+		time.Duration(12*time.Hour))
+	if err != nil {
+		jww.FATAL.Panicf("%+v", err)
+	}
+
+	if len(ephIDs) == 0 {
+		jww.FATAL.Panicf("No ephemeral ids found!")
+	}
+
+	return ephIDs
+}
+func getGatewayID(ndf *ndf.NetworkDefinition) *id.ID {
+	gateways := ndf.Gateways
+	gwID := viper.GetString(pickupGW)
+
+	if gwID == "" {
+		rng := csprng.NewSystemRNG()
+		i := randomness.ReadRangeUint32(0, uint32(len(gateways)), rng)
+		id, err := id.Unmarshal([]byte(gateways[i].ID))
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+		fmt.Printf("selected random gw: %s\n", id)
+		return id
+	}
+
+	for i := range gateways {
+		curID, _ := id.Unmarshal(gateways[i].ID)
+		jww.DEBUG.Printf("%s ==? %s", gwID, curID)
+		if curID.String() == gwID {
+			return curID
+		}
+	}
+
+	jww.FATAL.Panicf("%s is not a gateway in the NDF", gwID)
+	return nil
+}
+
+func getBloomFilter(targetGW string, ephID int64) *pb.ClientBlooms {
+	return nil
+}
+
+func getMessagesFromRound(targetGW *id.ID, roundID id.Round,
+	ephID ephemeral.Id, comms pickup.MessageRetrievalComms) (
+	*pb.GetMessagesResponse, error) {
+
+	host, ok := comms.GetHost(targetGW)
+	if !ok {
+		return nil, errors.Errorf("can't find host %s", targetGW)
+	}
+	msgReq := &pb.GetMessages{
+		ClientID: ephID[:],
+		RoundID:  uint64(roundID),
+		Target:   targetGW.Marshal(),
+	}
+
+	jww.DEBUG.Printf("Sending request: %+v", msgReq)
+
+	return comms.RequestMessages(host, msgReq)
+}