diff --git a/api/messenger/backup.go b/api/e2eApi/backup.go
similarity index 98%
rename from api/messenger/backup.go
rename to api/e2eApi/backup.go
index a5e73eaaf743030acacea195229e13e409f091e8..c547307fcac9c2f65267a8e32062f3cdd8f5bfc6 100644
--- a/api/messenger/backup.go
+++ b/api/e2eApi/backup.go
@@ -5,7 +5,7 @@
 // LICENSE file                                                               //
 ////////////////////////////////////////////////////////////////////////////////
 
-package messenger
+package e2eApi
 
 import "sync"
 
diff --git a/api/messenger/backupRestore.go b/api/e2eApi/backupRestore.go
similarity index 99%
rename from api/messenger/backupRestore.go
rename to api/e2eApi/backupRestore.go
index d3187f7fcc4b9f4535559351bc2d2ff68c7484ac..809ae8d13abaae38c51214030a5cf52fcc4748bb 100644
--- a/api/messenger/backupRestore.go
+++ b/api/e2eApi/backupRestore.go
@@ -1,7 +1,7 @@
 // FIXME: This is placeholder, there's got to be a better place to put
 // backup restoration than inside messenger.
 
-package messenger
+package e2eApi
 
 import (
 	"github.com/pkg/errors"
diff --git a/api/messenger/compress_test.go b/api/e2eApi/compress_test.go
similarity index 99%
rename from api/messenger/compress_test.go
rename to api/e2eApi/compress_test.go
index 19a4bcc414c112b9168b5da43b80f8e3d78721bf..052e3bb3d8abd88c1fa6e2c3a7f76c7eb9d9f171 100644
--- a/api/messenger/compress_test.go
+++ b/api/e2eApi/compress_test.go
@@ -5,7 +5,7 @@
 // LICENSE file                                                              //
 ///////////////////////////////////////////////////////////////////////////////
 
-package messenger
+package e2eApi
 
 import (
 	"bytes"
diff --git a/api/messenger/messenger.go b/api/e2eApi/e2eApi.go
similarity index 63%
rename from api/messenger/messenger.go
rename to api/e2eApi/e2eApi.go
index 2677e6fbaf23270e7eb6fc6009266da1638eaade..1b3f5d376edbe739b0ad145ed91be23dab89e2ac 100644
--- a/api/messenger/messenger.go
+++ b/api/e2eApi/e2eApi.go
@@ -1,8 +1,11 @@
-package messenger
+package e2eApi
 
 import (
 	"encoding/binary"
 	"encoding/json"
+	"gitlab.com/elixxir/client/storage/versioned"
+	"gitlab.com/elixxir/ekv"
+	"time"
 
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
@@ -18,12 +21,31 @@ import (
 
 type Client struct {
 	*api.Client
-	auth   auth.State
-	e2e    e2e.Handler
-	backup *Container
+	auth        auth.State
+	e2e         e2e.Handler
+	backup      *Container
+	e2eIdentity TransmissionIdentity
 }
 
-func Login(client *api.Client, callbacks auth.Callbacks) (m *Client, err error) {
+// Login creates a new e2eApi.Client backed by the api.Client persistent versioned.KV
+// If identity == nil, a new TransmissionIdentity will be generated automagically
+func Login(client *api.Client, callbacks auth.Callbacks,
+	identity *TransmissionIdentity) (m *Client, err error) {
+	return login(client, callbacks, identity, client.GetStorage().GetKV())
+}
+
+// LoginEphemeral creates a new e2eApi.Client backed by a totally ephemeral versioned.KV
+// If identity == nil, a new TransmissionIdentity will be generated automagically
+func LoginEphemeral(client *api.Client, callbacks auth.Callbacks,
+	identity *TransmissionIdentity) (m *Client, err error) {
+	return login(client, callbacks, identity, versioned.NewKV(ekv.MakeMemstore()))
+}
+
+// LoginLegacy creates a new e2eApi.Client backed by the api.Client persistent versioned.KV
+// Uses the pre-generated transmission ID used by api.Client
+// This function is designed to maintain backwards compatibility with previous xx messenger designs
+// and should not be used for other purposes
+func LoginLegacy(client *api.Client, callbacks auth.Callbacks) (m *Client, err error) {
 	m = &Client{
 		Client: client,
 		backup: &Container{},
@@ -41,6 +63,63 @@ func Login(client *api.Client, callbacks auth.Callbacks) (m *Client, err error)
 		return nil, err
 	}
 
+	u := m.Client.GetUser()
+	m.e2eIdentity = TransmissionIdentity{
+		ID:            u.TransmissionID,
+		RSAPrivatePem: u.TransmissionRSA,
+		Salt:          u.TransmissionSalt,
+		DHKeyPrivate:  u.E2eDhPrivateKey,
+	}
+
+	return m, err
+}
+
+// login creates a new e2eApi.Client backed by the given versioned.KV
+func login(client *api.Client, callbacks auth.Callbacks,
+	identity *TransmissionIdentity, kv *versioned.KV) (m *Client, err error) {
+	e2eGrp := client.GetStorage().GetE2EGroup()
+
+	// Create new identity automatically if one isn't specified
+	if identity == nil {
+		rng := client.GetRng().GetStream()
+		newIdentity, err := MakeTransmissionIdentity(rng, e2eGrp)
+		rng.Close()
+		if err != nil {
+			return nil, err
+		}
+		identity = &newIdentity
+		client.GetCmix().AddIdentity(identity.ID, time.Time{}, !kv.IsMemStore())
+	}
+
+	m = &Client{
+		Client:      client,
+		backup:      &Container{},
+		e2eIdentity: *identity,
+	}
+
+	//initialize the e2e storage
+	err = e2e.Init(kv, identity.ID, identity.DHKeyPrivate, e2eGrp,
+		rekey.GetDefaultEphemeralParams())
+	if err != nil {
+		return nil, err
+	}
+
+	//load the new e2e storage
+	m.e2e, err = e2e.Load(kv,
+		client.GetCmix(), identity.ID, e2eGrp, client.GetRng(),
+		client.GetEventReporter())
+	if err != nil {
+		return nil, errors.WithMessage(err, "Failed to load a "+
+			"newly created e2e store")
+	}
+
+	m.auth, err = auth.NewState(kv, client.GetCmix(),
+		m.e2e, client.GetRng(), client.GetEventReporter(),
+		auth.GetDefaultTemporaryParams(), callbacks, m.backup.TriggerBackup)
+	if err != nil {
+		return nil, err
+	}
+
 	return m, err
 }
 
@@ -52,7 +131,7 @@ func LoadOrInitE2e(client *api.Client) (e2e.Handler, error) {
 	e2eGrp := client.GetStorage().GetE2EGroup()
 	kv := client.GetStorage().GetKV()
 
-	//try to load a legacy e2e hander
+	//try to load a legacy e2e handler
 	e2eHandler, err := e2e.LoadLegacy(kv,
 		client.GetCmix(), usr.ReceptionID, e2eGrp, client.GetRng(),
 		client.GetEventReporter(), rekey.GetDefaultParams())
@@ -61,7 +140,7 @@ func LoadOrInitE2e(client *api.Client) (e2e.Handler, error) {
 		e2eHandler, err = e2e.Load(kv,
 			client.GetCmix(), usr.ReceptionID, e2eGrp, client.GetRng(),
 			client.GetEventReporter())
-		//if no new e2e handler exists, initilize an e2e user
+		//if no new e2e handler exists, initialize an e2e user
 		if err != nil {
 			jww.WARN.Printf("Failed to load e2e instance for %s, "+
 				"creating a new one", usr.ReceptionID)
@@ -102,6 +181,8 @@ func LoadOrInitE2e(client *api.Client) (e2e.Handler, error) {
 				return nil, errors.WithMessage(err, "Failed to load a "+
 					"newly created e2e store")
 			}
+
+			client.GetCmix().AddIdentity(usr.ReceptionID, time.Time{}, true)
 		} else {
 			jww.INFO.Printf("Loaded a modern e2e instance for %s",
 				usr.ReceptionID)
@@ -122,6 +203,11 @@ func (m *Client) GetUser() user.Info {
 	return u
 }
 
+// GetTransmissionIdentity returns a safe copy of the Client TransmissionIdentity
+func (m *Client) GetTransmissionIdentity() TransmissionIdentity {
+	return m.e2eIdentity.DeepCopy()
+}
+
 // ConstructProtoUserFile is a helper function which is used for proto
 // client testing.  This is used for development testing.
 func (m *Client) ConstructProtoUserFile() ([]byte, error) {
@@ -190,7 +276,7 @@ func (m *Client) DeleteContact(partnerId *id.ID) error {
 	// c.e2e.Conversations().Delete(partnerId)
 
 	// call delete requests to make sure nothing is lingering.
-	// this is for saftey to ensure the contact can be readded
+	// this is for safety to ensure the contact can be re-added
 	// in the future
 	_ = m.auth.DeleteRequest(partnerId)
 
diff --git a/api/e2eApi/identity.go b/api/e2eApi/identity.go
new file mode 100644
index 0000000000000000000000000000000000000000..dc9874ed6ce12e9e32c16697f8a63599ec1258a8
--- /dev/null
+++ b/api/e2eApi/identity.go
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package e2eApi
+
+import (
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/crypto/diffieHellman"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/crypto/xx"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+type TransmissionIdentity struct {
+	ID            *id.ID
+	RSAPrivatePem *rsa.PrivateKey
+	Salt          []byte
+	DHKeyPrivate  *cyclic.Int
+}
+
+// MakeTransmissionIdentity generates a new cryptographic identity for receiving messages
+func MakeTransmissionIdentity(rng csprng.Source, grp *cyclic.Group) (TransmissionIdentity, error) {
+	//make RSA Key
+	rsaKey, err := rsa.GenerateKey(rng,
+		rsa.DefaultRSABitLen)
+	if err != nil {
+		return TransmissionIdentity{}, err
+	}
+
+	//make salt
+	salt := make([]byte, 32)
+	_, err = rng.Read(salt)
+
+	//make dh private key
+	privKey := diffieHellman.GeneratePrivateKey(
+		len(grp.GetPBytes()),
+		grp, rng)
+
+	//make the ID
+	newId, err := xx.NewID(rsaKey.GetPublic(),
+		salt, id.User)
+	if err != nil {
+		return TransmissionIdentity{}, err
+	}
+
+	//create the identity object
+	I := TransmissionIdentity{
+		ID:            newId,
+		RSAPrivatePem: rsaKey,
+		Salt:          salt,
+		DHKeyPrivate:  privKey,
+	}
+
+	return I, nil
+}
+
+// DeepCopy produces a safe copy of a TransmissionIdentity
+func (t TransmissionIdentity) DeepCopy() TransmissionIdentity {
+	saltCopy := make([]byte, len(t.Salt))
+	copy(saltCopy, t.Salt)
+	return TransmissionIdentity{
+		ID:            t.ID.DeepCopy(),
+		RSAPrivatePem: t.RSAPrivatePem,
+		Salt:          saltCopy,
+		DHKeyPrivate:  t.DHKeyPrivate.DeepCopy(),
+	}
+}
diff --git a/api/messenger/notifications.go b/api/e2eApi/notifications.go
similarity index 99%
rename from api/messenger/notifications.go
rename to api/e2eApi/notifications.go
index 380029ebfaf68cdbe9b7310a731f6614b641d136..5a737563951c3d28ba40df7742be430eee731214 100644
--- a/api/messenger/notifications.go
+++ b/api/e2eApi/notifications.go
@@ -5,7 +5,7 @@
 // LICENSE file                                                              //
 ///////////////////////////////////////////////////////////////////////////////
 
-package messenger
+package e2eApi
 
 import (
 	"github.com/pkg/errors"
diff --git a/api/messenger/precan.go b/api/e2eApi/precan.go
similarity index 99%
rename from api/messenger/precan.go
rename to api/e2eApi/precan.go
index 16717bbfa04bb986e9069931da62126e700c5509..09343060746c34f8468a2f9a49d0c91bc7b5e02b 100644
--- a/api/messenger/precan.go
+++ b/api/e2eApi/precan.go
@@ -1,4 +1,4 @@
-package messenger
+package e2eApi
 
 import (
 	"encoding/binary"
diff --git a/api/messenger/utils.go b/api/e2eApi/utils.go
similarity index 99%
rename from api/messenger/utils.go
rename to api/e2eApi/utils.go
index 6e1ff51fd9be792124e30ee2a4e0487d3598efdc..8ec3be34d0d26701287dd7b356c2ca8a23a9ff76 100644
--- a/api/messenger/utils.go
+++ b/api/e2eApi/utils.go
@@ -6,7 +6,7 @@
 
 // Provides various utility functions for access over the bindings
 
-package messenger
+package e2eApi
 
 import (
 	"bytes"
diff --git a/api/identity.go b/api/identity.go
deleted file mode 100644
index 2ef5c5bb4cb5bfc468b368ab8d5f1ff4cf25b740..0000000000000000000000000000000000000000
--- a/api/identity.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package api
-
-import (
-	"gitlab.com/elixxir/crypto/cyclic"
-	"gitlab.com/elixxir/crypto/diffieHellman"
-	"gitlab.com/xx_network/crypto/csprng"
-	"gitlab.com/xx_network/crypto/signature/rsa"
-	"gitlab.com/xx_network/crypto/xx"
-	"gitlab.com/xx_network/primitives/id"
-)
-
-type Identity struct {
-	ID            *id.ID
-	RSAPrivatePem *rsa.PrivateKey
-	Salt          []byte
-	DHKeyPrivate  *cyclic.Int
-}
-
-// MakeIdentity generates a new cryptographic identity for receiving messages
-func MakeIdentity(rng csprng.Source, grp *cyclic.Group) (Identity, error) {
-	//make RSA Key
-	rsaKey, err := rsa.GenerateKey(rng,
-		rsa.DefaultRSABitLen)
-	if err != nil {
-		return Identity{}, err
-	}
-
-	//make salt
-	salt := make([]byte, 32)
-	_, err = rng.Read(salt)
-
-	//make dh private key
-	privkey := diffieHellman.GeneratePrivateKey(
-		len(grp.GetPBytes()),
-		grp, rng)
-
-	//make the ID
-	newId, err := xx.NewID(rsaKey.GetPublic(),
-		salt, id.User)
-	if err != nil {
-		return Identity{}, err
-	}
-
-	//create the identity object
-	I := Identity{
-		ID:            newId,
-		RSAPrivatePem: rsaKey,
-		Salt:          salt,
-		DHKeyPrivate:  privkey,
-	}
-
-	return I, nil
-}
diff --git a/auth/interface.go b/auth/interface.go
index e01e5576685fede4971dbcf8ccd542f08a464bb4..97c970f30effbbf484e52808dcd1421265d03a17 100644
--- a/auth/interface.go
+++ b/auth/interface.go
@@ -96,6 +96,12 @@ type State interface {
 	// VerifyOwnership checks if the received ownership proof is valid
 	VerifyOwnership(received, verified contact.Contact, e2e e2e.Handler) bool
 
+	// AddPartnerCallback that overrides the generic auth callback for the given partnerId
+	AddPartnerCallback(partnerId *id.ID, cb Callbacks)
+
+	// DeletePartnerCallback that overrides the generic auth callback for the given partnerId
+	DeletePartnerCallback(partnerId *id.ID)
+
 	//Closer stops listening to auth
 	io.Closer
 }
diff --git a/auth/partnerCallbacks.go b/auth/partnerCallbacks.go
new file mode 100644
index 0000000000000000000000000000000000000000..79264cf16c607d0dfebf2fbbb79b158b80ce35c9
--- /dev/null
+++ b/auth/partnerCallbacks.go
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package auth
+
+import (
+	"gitlab.com/xx_network/primitives/id"
+	"sync"
+)
+
+// partnerCallbacks is a thread-safe wrapper for Callbacks specific to partnerIds
+// For auth operations with a specific partner, these Callbacks will be used instead
+type partnerCallbacks struct {
+	callbacks map[id.ID]Callbacks
+	sync.RWMutex
+}
+
+// AddPartnerCallback that overrides the generic auth callback for the given partnerId
+func (p *partnerCallbacks) AddPartnerCallback(partnerId *id.ID, cb Callbacks) {
+	p.Lock()
+	defer p.Unlock()
+	if _, ok := p.callbacks[*partnerId]; !ok {
+		p.callbacks[*partnerId] = cb
+	}
+}
+
+// DeletePartnerCallback that overrides the generic auth callback for the given partnerId
+func (p *partnerCallbacks) DeletePartnerCallback(partnerId *id.ID) {
+	p.Lock()
+	defer p.Unlock()
+	if _, ok := p.callbacks[*partnerId]; ok {
+		delete(p.callbacks, *partnerId)
+	}
+}
+
+// getPartnerCallback returns the Callbacks for the given partnerId
+func (p *partnerCallbacks) getPartnerCallback(partnerId *id.ID) Callbacks {
+	return p.callbacks[*partnerId]
+}
diff --git a/auth/receivedConfirm.go b/auth/receivedConfirm.go
index 8ee24bd341ba6c03e2f00a7a5158d76558b6800e..538329e1b532a1a34d813f866dfb739319c09d3b 100644
--- a/auth/receivedConfirm.go
+++ b/auth/receivedConfirm.go
@@ -25,21 +25,21 @@ type receivedConfirmService struct {
 func (rcs *receivedConfirmService) Process(msg format.Message,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
 
-	state := rcs.s
+	authState := rcs.s
 
 	//parse the confirm
-	baseFmt, partnerPubKey, err := handleBaseFormat(msg, state.e2e.GetGroup())
+	baseFmt, partnerPubKey, err := handleBaseFormat(msg, authState.e2e.GetGroup())
 	if err != nil {
 		em := fmt.Sprintf("Failed to handle auth confirm: %s", err)
 		jww.WARN.Print(em)
-		state.event.Report(10, "Auth", "ConfirmError", em)
+		authState.event.Report(10, "Auth", "ConfirmError", em)
 		return
 	}
 
 	jww.TRACE.Printf("processing confirm: \n\t MYHISTORICALPUBKEY: %s\n\t"+
 		"MYPUBKEY: %s\n\t PARTNERPUBKEY: %s \n\t "+
 		"ECRPAYLOAD: %s \n\t MAC: %s",
-		state.e2e.GetHistoricalDHPubkey().TextVerbose(16, 0),
+		authState.e2e.GetHistoricalDHPubkey().TextVerbose(16, 0),
 		rcs.SentRequest.GetMyPubKey().TextVerbose(16, 0),
 		partnerPubKey.TextVerbose(16, 0),
 		base64.StdEncoding.EncodeToString(baseFmt.data),
@@ -47,13 +47,13 @@ func (rcs *receivedConfirmService) Process(msg format.Message,
 
 	// decrypt the payload
 	success, payload := cAuth.Decrypt(rcs.GetMyPrivKey(), partnerPubKey,
-		baseFmt.GetEcrPayload(), msg.GetMac(), state.e2e.GetGroup())
+		baseFmt.GetEcrPayload(), msg.GetMac(), authState.e2e.GetGroup())
 
 	if !success {
 		em := fmt.Sprintf("Received auth confirmation " +
 			"failed its mac check")
 		jww.WARN.Print(em)
-		state.event.Report(10, "Auth", "ConfirmError", em)
+		authState.event.Report(10, "Auth", "ConfirmError", em)
 		return
 	}
 
@@ -63,7 +63,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message,
 		em := fmt.Sprintf("Failed to unmarshal auth confirmation's "+
 			"encrypted payload: %s", err)
 		jww.WARN.Print(em)
-		state.event.Report(10, "Auth", "ConfirmError", em)
+		authState.event.Report(10, "Auth", "ConfirmError", em)
 		return
 	}
 
@@ -72,7 +72,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message,
 		em := fmt.Sprintf("Could not get auth conf SIDH Pubkey: %s",
 			err)
 		jww.WARN.Print(em)
-		state.event.Report(10, "Auth", "ConfirmError", em)
+		authState.event.Report(10, "Auth", "ConfirmError", em)
 		return
 	}
 
@@ -83,7 +83,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message,
 	// initial identity
 	if !cAuth.VerifyOwnershipProof(rcs.SentRequest.GetMyPrivKey(),
 		rcs.GetPartnerHistoricalPubKey(),
-		state.e2e.GetGroup(), ecrFmt.GetOwnership()) {
+		authState.e2e.GetGroup(), ecrFmt.GetOwnership()) {
 		jww.WARN.Printf("Failed authenticate identity for auth "+
 			"confirmation of %s", rcs.GetPartner())
 		return
@@ -91,7 +91,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message,
 
 	// add the partner
 	p := session.GetDefaultParams()
-	_, err = state.e2e.AddPartner(rcs.GetPartner(), partnerPubKey,
+	_, err = authState.e2e.AddPartner(rcs.GetPartner(), partnerPubKey,
 		rcs.GetMyPrivKey(), partnerSIDHPubKey, rcs.GetMySIDHPrivKey(), p, p)
 	if err != nil {
 		jww.WARN.Printf("Failed to create channel with partner %s and "+
@@ -103,7 +103,7 @@ func (rcs *receivedConfirmService) Process(msg format.Message,
 	}
 
 	// remove the service used for notifications of the confirm
-	state.net.DeleteService(receptionID.Source, rcs.notificationsService, nil)
+	authState.net.DeleteService(receptionID.Source, rcs.notificationsService, nil)
 
 	// callbacks
 	c := contact.Contact{
@@ -112,7 +112,14 @@ func (rcs *receivedConfirmService) Process(msg format.Message,
 		OwnershipProof: ecrFmt.GetOwnership(),
 		Facts:          make([]fact.Fact, 0),
 	}
-	state.callbacks.Confirm(c, receptionID, round)
+
+	authState.partnerCallbacks.RLock()
+	if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil {
+		cb.Confirm(c, receptionID, round)
+	} else {
+		authState.callbacks.Confirm(c, receptionID, round)
+	}
+	authState.partnerCallbacks.RUnlock()
 }
 
 func (rcs *receivedConfirmService) String() string {
diff --git a/auth/receivedRequest.go b/auth/receivedRequest.go
index 944c1880b3ac0a744331abbcbb44c4f8dba79949..b410b04a3e29cca95dedd8bc14d310507e8bce57 100644
--- a/auth/receivedRequest.go
+++ b/auth/receivedRequest.go
@@ -28,11 +28,11 @@ type receivedRequestService struct {
 
 func (rrs *receivedRequestService) Process(message format.Message,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
-	state := rrs.s
+	authState := rrs.s
 
 	// check if the timestamp is before the id was created and therefore
 	// should be ignored
-	tid, err := state.net.GetIdentity(receptionID.Source)
+	tid, err := authState.net.GetIdentity(receptionID.Source)
 	if err != nil {
 		jww.ERROR.Printf("received a request on %s which does not exist, "+
 			"this should not be possible: %+v", receptionID.Source.String(), err)
@@ -47,7 +47,7 @@ func (rrs *receivedRequestService) Process(message format.Message,
 
 	//decode the outer format
 	baseFmt, partnerPubKey, err := handleBaseFormat(
-		message, state.e2e.GetGroup())
+		message, authState.e2e.GetGroup())
 	if err != nil {
 		jww.WARN.Printf("Failed to handle auth request: %s", err)
 		return
@@ -57,15 +57,15 @@ func (rrs *receivedRequestService) Process(message format.Message,
 
 	jww.TRACE.Printf("processing requests: \n\t MYHISTORICALPUBKEY: %s "+
 		"\n\t PARTNERPUBKEY: %s \n\t ECRPAYLOAD: %s \n\t MAC: %s",
-		state.e2e.GetHistoricalDHPubkey().TextVerbose(16, 0),
+		authState.e2e.GetHistoricalDHPubkey().TextVerbose(16, 0),
 		partnerPubKey.TextVerbose(16, 0),
 		base64.StdEncoding.EncodeToString(baseFmt.data),
 		base64.StdEncoding.EncodeToString(message.GetMac()))
 
 	//Attempt to decrypt the payload
-	success, payload := cAuth.Decrypt(state.e2e.GetHistoricalDHPrivkey(),
+	success, payload := cAuth.Decrypt(authState.e2e.GetHistoricalDHPrivkey(),
 		partnerPubKey, baseFmt.GetEcrPayload(), message.GetMac(),
-		state.e2e.GetGroup())
+		authState.e2e.GetGroup())
 
 	if !success {
 		jww.WARN.Printf("Received auth request of %s failed its mac "+
@@ -97,11 +97,11 @@ func (rrs *receivedRequestService) Process(message format.Message,
 		format.DigestContents(message.GetContents()),
 		base64.StdEncoding.EncodeToString(fp))
 	jww.INFO.Print(em)
-	state.event.Report(1, "Auth", "RequestReceived", em)
+	authState.event.Report(1, "Auth", "RequestReceived", em)
 
 	// check the uniqueness of the request. Requests can be duplicated, so we
 	// must verify this is is not a duplicate, and drop if it is
-	newFP, position := state.store.CheckIfNegotiationIsNew(partnerID, fp)
+	newFP, position := authState.store.CheckIfNegotiationIsNew(partnerID, fp)
 
 	if !newFP {
 		// if its the newest, resend the confirm
@@ -113,10 +113,10 @@ func (rrs *receivedRequestService) Process(message format.Message,
 
 			// check if we already accepted, if we did, resend the confirm if
 			// we can load it
-			if _, err = state.e2e.GetPartner(partnerID); err != nil {
+			if _, err = authState.e2e.GetPartner(partnerID); err != nil {
 				//attempt to load the confirm, if we can, resend it
 				confirmPayload, mac, keyfp, err :=
-					state.store.LoadConfirmation(partnerID)
+					authState.store.LoadConfirmation(partnerID)
 				if err != nil {
 					jww.ERROR.Printf("Could not reconfirm a duplicate "+
 						"request of an accepted confirm from %s to %s because "+
@@ -125,14 +125,26 @@ func (rrs *receivedRequestService) Process(message format.Message,
 				}
 				// resend the confirm. It sends as a critical message, so errors
 				// do not need to be handled
-				_, _ = sendAuthConfirm(state.net, partnerID, keyfp,
-					confirmPayload, mac, state.event, state.params.ResetConfirmTag)
-			} else if state.params.ReplayRequests {
+				_, _ = sendAuthConfirm(authState.net, partnerID, keyfp,
+					confirmPayload, mac, authState.event, authState.params.ResetConfirmTag)
+			} else if authState.params.ReplayRequests {
 				//if we did not already accept, auto replay the request
 				if rrs.reset {
-					state.callbacks.Reset(c, receptionID, round)
+					authState.partnerCallbacks.RLock()
+					if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil {
+						cb.Reset(c, receptionID, round)
+					} else {
+						authState.callbacks.Reset(c, receptionID, round)
+					}
+					authState.partnerCallbacks.RUnlock()
 				} else {
-					state.callbacks.Request(c, receptionID, round)
+					authState.partnerCallbacks.RLock()
+					if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil {
+						cb.Request(c, receptionID, round)
+					} else {
+						authState.callbacks.Request(c, receptionID, round)
+					}
+					authState.partnerCallbacks.RUnlock()
 				}
 			}
 			//if not confirm, and params.replay requests is true, we need to replay
@@ -159,21 +171,21 @@ func (rrs *receivedRequestService) Process(message format.Message,
 		// error to see if it did or didnt exist
 		// Note: due to the newFP handling above, this can ONLY run in the event of
 		// a reset or when the partner doesnt exist, so it is safe
-		if err = state.e2e.DeletePartner(partnerID); err != nil {
+		if err = authState.e2e.DeletePartner(partnerID); err != nil {
 			if !strings.Contains(err.Error(), ratchet.NoPartnerErrorStr) {
 				jww.FATAL.Panicf("Failed to do actual partner deletion: %+v", err)
 			}
 		} else {
 			reset = true
-			_ = state.store.DeleteConfirmation(partnerID)
-			_ = state.store.DeleteSentRequest(partnerID)
+			_ = authState.store.DeleteConfirmation(partnerID)
+			_ = authState.store.DeleteSentRequest(partnerID)
 		}
 	}
 
 	// if a new, unique request is received when one already exists, delete the
 	// old one and process the new one
 	// this works because message pickup is generally time-sequential.
-	if err = state.store.DeleteReceivedRequest(partnerID); err != nil {
+	if err = authState.store.DeleteReceivedRequest(partnerID); err != nil {
 		if !strings.Contains(err.Error(), store.NoRequestFound) {
 			jww.FATAL.Panicf("Failed to delete old received request: %+v",
 				err)
@@ -187,7 +199,7 @@ func (rrs *receivedRequestService) Process(message format.Message,
 	// (SIDH keys have polarity, so both sent keys cannot be used together)
 	autoConfirm := false
 	bail := false
-	err = state.store.HandleSentRequest(partnerID,
+	err = authState.store.HandleSentRequest(partnerID,
 		func(request *store.SentRequest) error {
 
 			//if this code is running, then we know we sent a request and can
@@ -195,8 +207,8 @@ func (rrs *receivedRequestService) Process(message format.Message,
 			//This runner will auto delete the sent request if successful
 
 			//verify ownership proof
-			if !cAuth.VerifyOwnershipProof(state.e2e.GetHistoricalDHPrivkey(),
-				partnerPubKey, state.e2e.GetGroup(), ownershipProof) {
+			if !cAuth.VerifyOwnershipProof(authState.e2e.GetHistoricalDHPrivkey(),
+				partnerPubKey, authState.e2e.GetGroup(), ownershipProof) {
 				jww.WARN.Printf("Invalid ownership proof from %s to %s "+
 					"received, discarding msgDigest: %s, fp: %s",
 					partnerID, receptionID.Source,
@@ -233,25 +245,39 @@ func (rrs *receivedRequestService) Process(message format.Message,
 	// warning: the client will never be notified of the channel creation if a
 	// crash occurs after the store but before the conclusion of the callback
 	//create the auth storage
-	if err = state.store.AddReceived(c, partnerSIDHPubKey, round); err != nil {
+	if err = authState.store.AddReceived(c, partnerSIDHPubKey, round); err != nil {
 		em := fmt.Sprintf("failed to store contact Auth "+
 			"Request: %s", err)
 		jww.WARN.Print(em)
-		state.event.Report(10, "Auth", "RequestError", em)
+		authState.event.Report(10, "Auth", "RequestError", em)
 		return
 	}
 
-	//autoconfirm if we should
+	// auto-confirm if we should
+	authState.partnerCallbacks.RLock()
+	defer authState.partnerCallbacks.RUnlock()
 	if autoConfirm || reset {
-		_, _ = state.confirm(c, state.params.getConfirmTag(reset))
+		_, _ = authState.confirm(c, authState.params.getConfirmTag(reset))
 		//handle callbacks
 		if autoConfirm {
-			state.callbacks.Confirm(c, receptionID, round)
+			if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil {
+				cb.Confirm(c, receptionID, round)
+			} else {
+				authState.callbacks.Confirm(c, receptionID, round)
+			}
 		} else if reset {
-			state.callbacks.Reset(c, receptionID, round)
+			if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil {
+				cb.Reset(c, receptionID, round)
+			} else {
+				authState.callbacks.Reset(c, receptionID, round)
+			}
 		}
 	} else {
-		state.callbacks.Request(c, receptionID, round)
+		if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil {
+			cb.Request(c, receptionID, round)
+		} else {
+			authState.callbacks.Request(c, receptionID, round)
+		}
 	}
 }
 
diff --git a/auth/state.go b/auth/state.go
index 607275a3075372141b799cfab0e9a2aa73cebbdd..43e1d9735fde5bd0e5c4bbc396ec3ff74727c4b5 100644
--- a/auth/state.go
+++ b/auth/state.go
@@ -9,7 +9,6 @@ package auth
 
 import (
 	"encoding/base64"
-
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/auth/store"
 	"gitlab.com/elixxir/client/cmix"
@@ -24,7 +23,10 @@ import (
 
 // state is an implementation of the State interface.
 type state struct {
+	// Main Callbacks for all auth operations
 	callbacks Callbacks
+	// partner-specific Callbacks
+	partnerCallbacks partnerCallbacks
 
 	net cmixClient
 	e2e e2eHandler
@@ -70,13 +72,14 @@ func NewStateLegacy(kv *versioned.KV, net cmix.Client, e2e e2e.Handler,
 	callbacks Callbacks, backupTrigger func(reason string)) (State, error) {
 
 	s := &state{
-		callbacks:     callbacks,
-		net:           net,
-		e2e:           e2e,
-		rng:           rng,
-		event:         event,
-		params:        params,
-		backupTrigger: backupTrigger,
+		callbacks:        callbacks,
+		partnerCallbacks: partnerCallbacks{callbacks: make(map[id.ID]Callbacks)},
+		net:              net,
+		e2e:              e2e,
+		rng:              rng,
+		event:            event,
+		params:           params,
+		backupTrigger:    backupTrigger,
 	}
 
 	// create the store
@@ -112,7 +115,13 @@ func (s *state) CallAllReceivedRequests() {
 		rr := rrList[i]
 		eph := receptionID.BuildIdentityFromRound(rr.GetContact().ID,
 			rr.GetRound())
-		s.callbacks.Request(rr.GetContact(), eph, rr.GetRound())
+		s.partnerCallbacks.RLock()
+		if cb := s.partnerCallbacks.getPartnerCallback(rr.GetContact().ID); cb != nil {
+			cb.Request(rr.GetContact(), eph, rr.GetRound())
+		} else {
+			s.callbacks.Request(rr.GetContact(), eph, rr.GetRound())
+		}
+		s.partnerCallbacks.RUnlock()
 	}
 }
 
@@ -134,3 +143,13 @@ func (s *state) Close() error {
 	}, nil)
 	return nil
 }
+
+// AddPartnerCallback that overrides the generic auth callback for the given partnerId
+func (s *state) AddPartnerCallback(partnerId *id.ID, cb Callbacks) {
+	s.partnerCallbacks.AddPartnerCallback(partnerId, cb)
+}
+
+// DeletePartnerCallback that overrides the generic auth callback for the given partnerId
+func (s *state) DeletePartnerCallback(partnerId *id.ID) {
+	s.partnerCallbacks.DeletePartnerCallback(partnerId)
+}
diff --git a/backup/backup.go b/backup/backup.go
index 46f5bf73019e9c26e66b2a780f141a5b788647a4..4ade205c708b329346ab72cb062ca4851a90eb36 100644
--- a/backup/backup.go
+++ b/backup/backup.go
@@ -11,7 +11,7 @@ import (
 	"sync"
 	"time"
 
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/primitives/fact"
@@ -41,7 +41,7 @@ type Backup struct {
 	// Callback that is called with the encrypted backup when triggered
 	updateBackupCb UpdateBackupFn
 
-	container *messenger.Container
+	container *e2eApi.Container
 
 	jsonParams string
 
@@ -91,7 +91,7 @@ type UpdateBackupFn func(encryptedBackup []byte)
 // Call this to turn on backups for the first time or to replace the user's
 // password.
 func InitializeBackup(password string, updateBackupCb UpdateBackupFn,
-	container *messenger.Container, e2e E2e, session Session, ud UserDiscovery,
+	container *e2eApi.Container, e2e E2e, session Session, ud UserDiscovery,
 	kv *versioned.KV, rng *fastRNG.StreamGenerator) (*Backup, error) {
 	b := &Backup{
 		updateBackupCb: updateBackupCb,
@@ -135,7 +135,7 @@ func InitializeBackup(password string, updateBackupCb UpdateBackupFn,
 // ResumeBackup resumes a backup by restoring the Backup object and registering
 // a new callback. Call this to resume backups that have already been
 // initialized. Returns an error if backups have not already been initialized.
-func ResumeBackup(updateBackupCb UpdateBackupFn, container *messenger.Container,
+func ResumeBackup(updateBackupCb UpdateBackupFn, container *e2eApi.Container,
 	e2e E2e, session Session, ud UserDiscovery, kv *versioned.KV,
 	rng *fastRNG.StreamGenerator) (*Backup, error) {
 	_, _, _, err := loadBackup(kv)
diff --git a/backup/backup_test.go b/backup/backup_test.go
index f61dc0a06b02adcaa3218c909a90dda6c90c3985..5e353b627ee449233db9308ec3166d4f381b9a28 100644
--- a/backup/backup_test.go
+++ b/backup/backup_test.go
@@ -14,7 +14,7 @@ import (
 	"testing"
 	"time"
 
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"gitlab.com/elixxir/client/storage"
 	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/ekv"
@@ -32,7 +32,7 @@ func Test_InitializeBackup(t *testing.T) {
 	cbChan := make(chan []byte, 2)
 	cb := func(encryptedBackup []byte) { cbChan <- encryptedBackup }
 	expectedPassword := "MySuperSecurePassword"
-	b, err := InitializeBackup(expectedPassword, cb, &messenger.Container{},
+	b, err := InitializeBackup(expectedPassword, cb, &e2eApi.Container{},
 		newMockE2e(t),
 		newMockSession(t), newMockUserDiscovery(), kv, rngGen)
 	if err != nil {
@@ -84,7 +84,7 @@ func Test_ResumeBackup(t *testing.T) {
 	cbChan1 := make(chan []byte)
 	cb1 := func(encryptedBackup []byte) { cbChan1 <- encryptedBackup }
 	expectedPassword := "MySuperSecurePassword"
-	b, err := InitializeBackup(expectedPassword, cb1, &messenger.Container{},
+	b, err := InitializeBackup(expectedPassword, cb1, &e2eApi.Container{},
 		newMockE2e(t), newMockSession(t), newMockUserDiscovery(), kv, rngGen)
 	if err != nil {
 		t.Errorf("Failed to initialize new Backup: %+v", err)
@@ -106,7 +106,7 @@ func Test_ResumeBackup(t *testing.T) {
 	// Resume the backup with a new callback
 	cbChan2 := make(chan []byte)
 	cb2 := func(encryptedBackup []byte) { cbChan2 <- encryptedBackup }
-	b2, err := ResumeBackup(cb2, &messenger.Container{}, newMockE2e(t), newMockSession(t),
+	b2, err := ResumeBackup(cb2, &e2eApi.Container{}, newMockE2e(t), newMockSession(t),
 		newMockUserDiscovery(), kv, rngGen)
 	if err != nil {
 		t.Errorf("ResumeBackup returned an error: %+v", err)
@@ -149,7 +149,7 @@ func Test_resumeBackup_NoKeyError(t *testing.T) {
 	expectedErr := "object not found"
 	s := storage.InitTestingSession(t)
 	rngGen := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG)
-	_, err := ResumeBackup(nil, &messenger.Container{}, newMockE2e(t), newMockSession(t),
+	_, err := ResumeBackup(nil, &e2eApi.Container{}, newMockE2e(t), newMockSession(t),
 		newMockUserDiscovery(), s.GetKV(), rngGen)
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("ResumeBackup did not return the expected error when no "+
@@ -392,7 +392,7 @@ func newTestBackup(password string, cb UpdateBackupFn, t *testing.T) *Backup {
 	b, err := InitializeBackup(
 		password,
 		cb,
-		&messenger.Container{},
+		&e2eApi.Container{},
 		newMockE2e(t),
 		newMockSession(t),
 		newMockUserDiscovery(),
@@ -416,7 +416,7 @@ func Benchmark_InitializeBackup(t *testing.B) {
 	expectedPassword := "MySuperSecurePassword"
 	for i := 0; i < t.N; i++ {
 		_, err := InitializeBackup(expectedPassword, cb,
-			&messenger.Container{},
+			&e2eApi.Container{},
 			newMockE2e(t),
 			newMockSession(t), newMockUserDiscovery(), kv, rngGen)
 		if err != nil {
diff --git a/bindings/connect.go b/bindings/connect.go
index 9910af952b93165e769b15e9e54d508adcf378e3..a4ba6f9c785dda71ac582194829690811830fdd1 100644
--- a/bindings/connect.go
+++ b/bindings/connect.go
@@ -32,7 +32,7 @@ func (c *Connection) GetId() int {
 // partner.Manager is confirmed.
 // recipientContact - marshalled contact.Contact object
 // myIdentity - marshalled Identity object
-func (c *Client) Connect(recipientContact []byte, myIdentity []byte) (
+func (c *Client) Connect(e2eClientId int, recipientContact []byte) (
 	*Connection, error) {
 	cont, err := contact.Unmarshal(recipientContact)
 	if err != nil {
@@ -43,8 +43,7 @@ func (c *Client) Connect(recipientContact []byte, myIdentity []byte) (
 		return nil, err
 	}
 
-	connection, err := connect.Connect(cont, myID, myDHPriv, c.api.GetRng(),
-		c.api.GetStorage().GetE2EGroup(), c.api.GetCmix(), connect.GetDefaultParams())
+	connection, err := connect.Connect(cont, "test", connect.GetDefaultParams())
 
 	if err != nil {
 		return nil, err
diff --git a/cmd/callbacks.go b/cmd/callbacks.go
index 07a4c66acd68cd14601ea63d32d9a30ddce28b4f..3d2050e4161e5615a26be8e52f0a4c462c140447 100644
--- a/cmd/callbacks.go
+++ b/cmd/callbacks.go
@@ -10,7 +10,7 @@ package cmd
 
 import (
 	"fmt"
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/catalog"
@@ -25,10 +25,10 @@ import (
 type authCallbacks struct {
 	autoConfirm bool
 	confCh      chan *id.ID
-	client      *messenger.Client
+	client      *e2eApi.Client
 }
 
-func makeAuthCallbacks(client *messenger.Client, autoConfirm bool) *authCallbacks {
+func makeAuthCallbacks(client *e2eApi.Client, autoConfirm bool) *authCallbacks {
 	return &authCallbacks{
 		autoConfirm: autoConfirm,
 		confCh:      make(chan *id.ID, 10),
@@ -71,7 +71,7 @@ func (a *authCallbacks) Reset(requestor contact.Contact,
 	fmt.Printf(msg)
 }
 
-func registerMessageListener(client *messenger.Client) chan receive.Message {
+func registerMessageListener(client *e2eApi.Client) chan receive.Message {
 	recvCh := make(chan receive.Message, 10000)
 	listenerID := client.GetE2E().RegisterChannel("DefaultCLIReceiver",
 		receive.AnyUser(), catalog.NoType, recvCh)
diff --git a/cmd/fileTransfer.go b/cmd/fileTransfer.go
index f09380f10c71289fc07fb05e0f7d7d77d27daa9b..ae7c3d84206678e89f2b9e3c92bdea7de076b901 100644
--- a/cmd/fileTransfer.go
+++ b/cmd/fileTransfer.go
@@ -9,7 +9,7 @@ package cmd
 
 import (
 	"fmt"
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"io/ioutil"
 	"time"
 
@@ -132,7 +132,7 @@ type receivedFtResults struct {
 // initFileTransferManager creates a new file transfer manager with a new
 // reception callback. Returns the file transfer manager and the channel that
 // will be triggered when the callback is called.
-func initFileTransferManager(client *messenger.Client, maxThroughput int) (
+func initFileTransferManager(client *e2eApi.Client, maxThroughput int) (
 	*ftE2e.Wrapper, chan receivedFtResults) {
 
 	// Create interfaces.ReceiveCallback that returns the results on a channel
diff --git a/cmd/group.go b/cmd/group.go
index fce4f5989c733a0fe2b7fa3f72ab625d29797698..6eb63d5571545070d7fa3b73682687ddbdefddd9 100644
--- a/cmd/group.go
+++ b/cmd/group.go
@@ -12,7 +12,7 @@ package cmd
 import (
 	"bufio"
 	"fmt"
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"gitlab.com/elixxir/client/cmix/identity/receptionID"
 	"gitlab.com/elixxir/client/cmix/rounds"
 	"gitlab.com/elixxir/primitives/format"
@@ -115,7 +115,7 @@ var groupCmd = &cobra.Command{
 
 // initGroupManager creates a new group chat manager and starts the process
 // service.
-func initGroupManager(client *messenger.Client) (groupChat.GroupChat,
+func initGroupManager(client *e2eApi.Client) (groupChat.GroupChat,
 	chan groupChat.MessageReceive, chan groupStore.Group) {
 	recChan := make(chan groupChat.MessageReceive, 10)
 
diff --git a/cmd/init.go b/cmd/init.go
index ad48ca4850e5046c89b6e7b73ea212cad77856d9..6be506aa662f6d79bdaf5dbaa75c7b09c338200a 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -14,17 +14,17 @@ import (
 	"github.com/spf13/cobra"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 )
 
 // initCmd creates a new user object with the given NDF
 var initCmd = &cobra.Command{
 	Use:   "init",
-	Short: ("Initialize a user ID but do not connect to the network"),
+	Short: "Initialize a user ID but do not connect to the network",
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
 		client := createClient()
-		e2e, err := messenger.LoadOrInitE2e(client)
+		e2e, err := e2eApi.LoadOrInitE2e(client)
 		if err != nil {
 			jww.FATAL.Panicf("%+v", err)
 		}
diff --git a/cmd/precan.go b/cmd/precan.go
index 4a4050efe99237135d5dfb3c9aaf028f3d77974f..e7574b45853f15428f2ce619e5891cd8b2125849 100644
--- a/cmd/precan.go
+++ b/cmd/precan.go
@@ -11,7 +11,7 @@ package cmd
 
 import (
 	"encoding/binary"
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"strconv"
 
 	jww "github.com/spf13/jwalterweatherman"
@@ -67,7 +67,7 @@ func getPrecanID(recipientID *id.ID) uint {
 	return uint(recipientID.Bytes()[7])
 }
 
-func addPrecanAuthenticatedChannel(client *messenger.Client, recipientID *id.ID,
+func addPrecanAuthenticatedChannel(client *e2eApi.Client, recipientID *id.ID,
 	recipient contact.Contact) {
 	jww.WARN.Printf("Precanned user id detected: %s", recipientID)
 	preUsr, err := client.MakePrecannedAuthenticatedChannel(
diff --git a/cmd/root.go b/cmd/root.go
index 8bdd2944b77cb12258a4d73804b8ec22d3377c6a..c3eb8e13ac2df23665b4dd800178ff551d236597 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -25,7 +25,7 @@ import (
 	"sync"
 	"time"
 
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"gitlab.com/elixxir/client/backup"
 	"gitlab.com/elixxir/client/e2e"
 
@@ -559,7 +559,7 @@ func createClient() *api.Client {
 			}
 
 			// Construct client from backup data
-			backupIdList, _, err := messenger.NewClientFromBackup(string(ndfJSON), storeDir,
+			backupIdList, _, err := e2eApi.NewClientFromBackup(string(ndfJSON), storeDir,
 				pass, backupPass, backupFile)
 			if err != nil {
 				jww.FATAL.Panicf("%+v", err)
@@ -621,7 +621,7 @@ func initParams() api.Params {
 	return p
 }
 
-func initClient() *messenger.Client {
+func initClient() *e2eApi.Client {
 	createClient()
 
 	pass := parsePassword(viper.GetString("password"))
@@ -640,7 +640,7 @@ func initClient() *messenger.Client {
 	authCbs = makeAuthCallbacks(nil,
 		viper.GetBool("unsafe-channel-creation"))
 
-	client, err := messenger.Login(baseclient, authCbs)
+	client, err := e2eApi.LoginLegacy(baseclient, authCbs)
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
@@ -707,7 +707,7 @@ func initClient() *messenger.Client {
 	return client
 }
 
-func acceptChannel(client *messenger.Client, recipientID *id.ID) {
+func acceptChannel(client *e2eApi.Client, recipientID *id.ID) {
 	recipientContact, err := client.GetAuth().GetReceivedRequest(
 		recipientID)
 	if err != nil {
@@ -720,14 +720,14 @@ func acceptChannel(client *messenger.Client, recipientID *id.ID) {
 	}
 }
 
-func deleteChannel(client *messenger.Client, partnerId *id.ID) {
+func deleteChannel(client *e2eApi.Client, partnerId *id.ID) {
 	err := client.DeleteContact(partnerId)
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
 }
 
-func addAuthenticatedChannel(client *messenger.Client, recipientID *id.ID,
+func addAuthenticatedChannel(client *e2eApi.Client, recipientID *id.ID,
 	recipient contact.Contact) {
 	var allowed bool
 	if viper.GetBool("unsafe-channel-creation") {
@@ -764,7 +764,7 @@ func addAuthenticatedChannel(client *messenger.Client, recipientID *id.ID,
 	}
 }
 
-func resetAuthenticatedChannel(client *messenger.Client, recipientID *id.ID,
+func resetAuthenticatedChannel(client *e2eApi.Client, recipientID *id.ID,
 	recipient contact.Contact) {
 	var allowed bool
 	if viper.GetBool("unsafe-channel-creation") {
@@ -1036,7 +1036,7 @@ func init() {
 	viper.BindPFlag("log", rootCmd.PersistentFlags().Lookup("log"))
 
 	rootCmd.Flags().StringP("regcode", "", "",
-		"Identity code (optional)")
+		"TransmissionIdentity code (optional)")
 	viper.BindPFlag("regcode", rootCmd.Flags().Lookup("regcode"))
 
 	rootCmd.PersistentFlags().StringP("message", "m", "",
diff --git a/cmix/identity/tracker.go b/cmix/identity/tracker.go
index 18e68d2f7bc34eab98affbf202e88c1a1efef296..7d9b7aaae3cbae55e8a7b5d83ed06b59649807c5 100644
--- a/cmix/identity/tracker.go
+++ b/cmix/identity/tracker.go
@@ -103,21 +103,8 @@ func NewOrLoadTracker(session storage.Session, addrSpace address.Space) *manager
 			})
 		} else {
 			jww.WARN.Printf("No tracked identities found and no legacy " +
-				"stored timestamp found; creating a new tracked identity " +
-				"from scratch.")
-
-			t.tracked = append(t.tracked, TrackedID{
-				// Make the next generation now so a generation triggers on
-				// first run
-				NextGeneration: netTime.Now(),
-				// Start generation 24 hours ago to make sure all resent
-				// ephemeral do pickups
-				// TODO: Should we go back farther?
-				LastGeneration: netTime.Now().Add(-time.Duration(ephemeral.Period)),
-				Source:         t.session.GetReceptionID(),
-				ValidUntil:     Forever,
-				Persistent:     true,
-			})
+				"stored timestamp found; no messages can be picked up until an " +
+				"identity is added.")
 		}
 	} else if err != nil {
 		jww.FATAL.Panicf("Unable to create new Tracker: %+v", err)
diff --git a/connect/authenticated.go b/connect/authenticated.go
index 518bbc2eb482f12c23005071e4e81f42c47caf38..f12e4246016692d7b0aec8d0483f16d47314176b 100644
--- a/connect/authenticated.go
+++ b/connect/authenticated.go
@@ -10,6 +10,7 @@ package connect
 import (
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"gitlab.com/elixxir/client/catalog"
 	"gitlab.com/elixxir/client/cmix"
 	clientE2e "gitlab.com/elixxir/client/e2e"
@@ -51,24 +52,23 @@ type AuthenticatedCallback func(connection AuthenticatedConnection)
 // ConnectWithAuthentication is called by the client, ie the one establishing
 // connection with the server. Once a connect.Connection has been established
 // with the server and then authenticate their identity to the server.
-func ConnectWithAuthentication(recipient contact.Contact, myId *id.ID,
-	salt []byte, myRsaPrivKey *rsa.PrivateKey, myDhPrivKey *cyclic.Int,
-	rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client,
+func ConnectWithAuthentication(recipient contact.Contact, e2eClient *e2eApi.Client,
 	p Params) (AuthenticatedConnection, error) {
 
 	// Track the time since we started to attempt to establish a connection
 	timeStart := netTime.Now()
 
 	// Establish a connection with the server
-	conn, err := Connect(recipient, myId, myDhPrivKey, rng, grp, net, p)
+	conn, err := Connect(recipient, e2eClient, p)
 	if err != nil {
 		return nil, errors.Errorf("failed to establish connection "+
 			"with recipient %s: %+v", recipient.ID, err)
 	}
 
 	// Build the authenticated connection and return
-	return connectWithAuthentication(conn, timeStart, recipient, salt, myRsaPrivKey,
-		rng, net, p)
+	identity := e2eClient.GetTransmissionIdentity()
+	return connectWithAuthentication(conn, timeStart, recipient, identity.Salt, identity.RSAPrivatePem,
+		e2eClient.GetRng(), e2eClient.GetCmix(), p)
 }
 
 // connectWithAuthentication builds and sends an IdentityAuthentication to
diff --git a/connect/connect.go b/connect/connect.go
index 0de11c560d74c6e085fe5fc3276cedb69a81490c..10dc397137b53e060fc29077eab6d8f646283197 100644
--- a/connect/connect.go
+++ b/connect/connect.go
@@ -8,6 +8,12 @@ package connect
 
 import (
 	"encoding/json"
+	"gitlab.com/elixxir/client/api/e2eApi"
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/storage/versioned"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/elixxir/ekv"
 	"io"
 	"time"
 
@@ -15,7 +21,6 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/auth"
 	"gitlab.com/elixxir/client/catalog"
-	"gitlab.com/elixxir/client/cmix"
 	"gitlab.com/elixxir/client/cmix/identity/receptionID"
 	"gitlab.com/elixxir/client/cmix/rounds"
 	clientE2e "gitlab.com/elixxir/client/e2e"
@@ -23,12 +28,8 @@ import (
 	"gitlab.com/elixxir/client/e2e/receive"
 	"gitlab.com/elixxir/client/e2e/rekey"
 	"gitlab.com/elixxir/client/event"
-	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/crypto/contact"
-	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/e2e"
-	"gitlab.com/elixxir/crypto/fastRNG"
-	"gitlab.com/elixxir/ekv"
 	"gitlab.com/xx_network/primitives/id"
 )
 
@@ -118,41 +119,19 @@ func GetParameters(params string) (Params, error) {
 // and returns a Connection object for the newly-created partner.Manager
 // This function is to be used sender-side and will block until the
 // partner.Manager is confirmed.
-func Connect(recipient contact.Contact, myId *id.ID, privKey *cyclic.Int,
-	rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client,
+func Connect(recipient contact.Contact, e2eClient *e2eApi.Client,
 	p Params) (Connection, error) {
-	//add the identity
-	net.AddIdentity(myId, time.Time{}, false)
-
-	// Build an ephemeral KV
-	kv := versioned.NewKV(ekv.MakeMemstore())
-
-	// Build E2e handler
-	err := clientE2e.Init(kv, myId, privKey, grp, p.Rekey)
-	if err != nil {
-		return nil, err
-	}
-	e2eHandler, err := clientE2e.Load(kv, net, myId, grp, rng, p.Event)
-	if err != nil {
-		return nil, err
-	}
 
 	// Build callback for E2E negotiation
 	signalChannel := make(chan Connection, 1)
 	cb := func(connection Connection) {
 		signalChannel <- connection
 	}
-	callback := getAuthCallback(cb, nil, e2eHandler, p)
-
-	// Build auth object for E2E negotiation
-	authState, err := auth.NewState(kv, net, e2eHandler,
-		rng, p.Event, p.Auth, callback, nil)
-	if err != nil {
-		return nil, err
-	}
+	callback := getAuthCallback(cb, nil, e2eClient.GetE2E(), e2eClient.GetAuth(), p)
+	e2eClient.GetAuth().AddPartnerCallback(recipient.ID, callback)
 
 	// Perform the auth request
-	_, err = authState.Request(recipient, nil)
+	_, err := e2eClient.GetAuth().Request(recipient, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -199,7 +178,7 @@ func StartServer(cb Callback, myId *id.ID, privKey *cyclic.Int,
 	}
 
 	// Build callback for E2E negotiation
-	callback := getAuthCallback(nil, cb, e2eHandler, p)
+	callback := getAuthCallback(nil, cb, e2eHandler, nil, p)
 
 	// Build auth object for E2E negotiation
 	authState, err := auth.NewState(kv, net, e2eHandler,
@@ -269,7 +248,7 @@ func (h *handler) Unregister(listenerID receive.ListenerID) {
 // building new Connection objects when an auth Request is received.
 type authCallback struct {
 	// Used for signaling confirmation of E2E partnership
-	confrimCallback Callback
+	confirmCallback Callback
 	requestCallback Callback
 
 	// Used for building new Connection objects
@@ -282,12 +261,13 @@ type authCallback struct {
 // of an auth.State object.
 // it will accept requests only if a request callback is passed in
 func getAuthCallback(confirm, request Callback, e2e clientE2e.Handler,
-	params Params) *authCallback {
+	auth auth.State, params Params) *authCallback {
 	return &authCallback{
-		confrimCallback:  confirm,
+		confirmCallback:  confirm,
 		requestCallback:  request,
 		connectionE2e:    e2e,
 		connectionParams: params,
+		authState:        auth,
 	}
 }
 
@@ -296,6 +276,7 @@ func (a authCallback) Confirm(requestor contact.Contact,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
 	jww.DEBUG.Printf("Connection auth request for %s confirmed",
 		requestor.ID.String())
+	defer a.authState.DeletePartnerCallback(requestor.ID)
 
 	// After confirmation, get the new partner
 	newPartner, err := a.connectionE2e.GetPartner(requestor.ID)
@@ -303,26 +284,24 @@ func (a authCallback) Confirm(requestor contact.Contact,
 		jww.ERROR.Printf("Unable to build connection with "+
 			"partner %s: %+v", requestor.ID, err)
 		// Send a nil connection to avoid hold-ups down the line
-		if a.confrimCallback != nil {
-			a.confrimCallback(nil)
+		if a.confirmCallback != nil {
+			a.confirmCallback(nil)
 		}
-
 		return
 	}
 
 	// Return the new Connection object
-	if a.confrimCallback != nil {
-		a.confrimCallback(BuildConnection(newPartner, a.connectionE2e,
+	if a.confirmCallback != nil {
+		a.confirmCallback(BuildConnection(newPartner, a.connectionE2e,
 			a.authState, a.connectionParams))
 	}
-
 }
 
 // Request will be called when an auth Request message is processed.
 func (a authCallback) Request(requestor contact.Contact,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
 	if a.requestCallback == nil {
-		jww.ERROR.Printf("Recieved a request when requests are" +
+		jww.ERROR.Printf("Received a request when requests are" +
 			"not enable, will not accept")
 	}
 	_, err := a.authState.Confirm(requestor)
@@ -371,7 +350,7 @@ func (h *handler) PartitionSize(payloadIndex uint) uint {
 	return h.e2e.PartitionSize(payloadIndex)
 }
 
-// PayloadSize Returns the max payload size for a partitionable E2E
+// PayloadSize Returns the max payload size for a partition-able E2E
 // message
 func (h *handler) PayloadSize() uint {
 	return h.e2e.PayloadSize()
diff --git a/storage/user/info.go b/storage/user/info.go
index 11d9381ef07cbad22a91dc49db8cff48d551fed8..62603c2225f0921f54684299f140fb33bfb6d182 100644
--- a/storage/user/info.go
+++ b/storage/user/info.go
@@ -105,7 +105,7 @@ func (u *User) PortableUserInfo() Info {
 		ReceptionRSA:          ci.GetReceptionRSA(),
 		Precanned:             ci.IsPrecanned(),
 		//fixme: set these in the e2e layer, the command line layer
-		//needs more logical seperation so this can be removed
+		//needs more logical separation so this can be removed
 		E2eDhPrivateKey: nil,
 		E2eDhPublicKey:  nil,
 	}
diff --git a/xxmutils/restoreContacts.go b/xxmutils/restoreContacts.go
index 96e9c2b1b8d4fd26680bef805f27bbdb712d71b4..f17789206e3eb58fff0305db12f09ef9c1c9cd8e 100644
--- a/xxmutils/restoreContacts.go
+++ b/xxmutils/restoreContacts.go
@@ -11,7 +11,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"gitlab.com/elixxir/client/api/messenger"
+	"gitlab.com/elixxir/client/api/e2eApi"
 	"gitlab.com/elixxir/client/single"
 	"gitlab.com/xx_network/primitives/netTime"
 	"math"
@@ -37,7 +37,7 @@ import (
 // xxDK users should not use this function. This function is used by
 // the mobile phone apps and are not intended to be part of the xxDK. It
 // should be treated as internal functions specific to the phone apps.
-func RestoreContactsFromBackup(backupPartnerIDs []byte, client *messenger.Client,
+func RestoreContactsFromBackup(backupPartnerIDs []byte, client *e2eApi.Client,
 	udManager *ud.Manager,
 	updatesCb interfaces.RestoreContactsUpdater) ([]*id.ID, []*id.ID,
 	[]error, error) {
@@ -178,7 +178,7 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *messenger.Client
 // the mobile phone apps and are not intended to be part of the xxDK. It
 // should be treated as internal functions specific to the phone apps.
 func LookupContacts(in chan *id.ID, out chan *contact.Contact,
-	failCh chan failure, client *messenger.Client, udContact contact.Contact,
+	failCh chan failure, client *e2eApi.Client, udContact contact.Contact,
 	wg *sync.WaitGroup) {
 	defer wg.Done()
 	// Start looking up contacts with user discovery and feed this
@@ -205,7 +205,7 @@ func LookupContacts(in chan *id.ID, out chan *contact.Contact,
 // the mobile phone apps and are not intended to be part of the xxDK. It
 // should be treated as internal functions specific to the phone apps.
 func ResetSessions(in, out chan *contact.Contact, failCh chan failure,
-	client *messenger.Client, wg *sync.WaitGroup) {
+	client *e2eApi.Client, wg *sync.WaitGroup) {
 	defer wg.Done()
 	for c := range in {
 		_, err := client.GetAuth().Reset(*c)
@@ -224,7 +224,7 @@ func ResetSessions(in, out chan *contact.Contact, failCh chan failure,
 // xxDK users should not use this function. This function is used by
 // the mobile phone apps and are not intended to be part of the xxDK. It
 // should be treated as internal functions specific to the phone apps.
-func LookupContact(userID *id.ID, client *messenger.Client, udContact contact.Contact) (
+func LookupContact(userID *id.ID, client *e2eApi.Client, udContact contact.Contact) (
 	*contact.Contact, error) {
 	// This is a little wonky, but wait until we get called then
 	// set the result to the contact objects details if there is