diff --git a/bindings/e2eHandler.go b/bindings/e2eHandler.go
index 8366299d37676a2d8f43878c66b7eedc8e347cfd..52e0c830f385d760e83d792b490e2c7acb0a07f4 100644
--- a/bindings/e2eHandler.go
+++ b/bindings/e2eHandler.go
@@ -206,7 +206,8 @@ type messageProcessor struct {
 	bindingsCbs Processor
 }
 
-// convertAuthCallbacks turns an auth.Callbacks into an AuthCallbacks
+// convertProcessor turns the input of a message.Processor to the
+// binding-layer primitives equivalents within the Processor.Process.
 func convertProcessor(msg format.Message,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) (
 	message []byte, receptionId []byte, ephemeralId int64, roundId int64) {
diff --git a/bindings/group.go b/bindings/group.go
index 95f91744f2800674303ebd12a6bf9a7284ca3c0a..b305cc61bc69724282ced7cb08ba57a2870c088c 100644
--- a/bindings/group.go
+++ b/bindings/group.go
@@ -1 +1,430 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
 package bindings
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	gc "gitlab.com/elixxir/client/groupChat"
+	gs "gitlab.com/elixxir/client/groupChat/groupStore"
+	"gitlab.com/elixxir/crypto/group"
+	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/xx_network/primitives/id"
+	"sync"
+	"time"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Group Chat Singleton Tracker                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+// groupChatTrackerSingleton is used to track GroupChat objects so that they can be
+// referenced by ID back over the bindings.
+var groupChatTrackerSingleton = &groupChatTracker{
+	tracked: make(map[int]*GroupChat),
+	count:   0,
+}
+
+// groupChatTracker is a singleton used to keep track of extant GroupChat objects,
+// preventing race conditions created by passing it over the bindings.
+type groupChatTracker struct {
+	tracked map[int]*GroupChat
+	count   int
+	mux     sync.RWMutex
+}
+
+// make create a GroupChat from a groupChat.Wrapper, assigns it a unique ID, and
+// adds it to the udTracker.
+func (ut *groupChatTracker) make(gcInt gc.GroupChat) *GroupChat {
+	ut.mux.Lock()
+	defer ut.mux.Unlock()
+
+	id := ut.count
+	ut.count++
+
+	ut.tracked[id] = &GroupChat{
+		m:  gc.NewWrapper(gcInt),
+		id: id,
+	}
+
+	return ut.tracked[id]
+}
+
+// get an GroupChat from the groupChatTracker given its ID.
+func (ut *groupChatTracker) get(id int) (*GroupChat, error) {
+	ut.mux.RLock()
+	defer ut.mux.RUnlock()
+
+	c, exist := ut.tracked[id]
+	if !exist {
+		return nil, errors.Errorf(
+			"Cannot get UserDiscovery for ID %d, does not exist", id)
+	}
+
+	return c, nil
+}
+
+// delete removes a GroupChat from the groupChatTracker.
+func (ut *groupChatTracker) delete(id int) {
+	ut.mux.Lock()
+	defer ut.mux.Unlock()
+
+	delete(ut.tracked, id)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Group Chat                                                                 //
+////////////////////////////////////////////////////////////////////////////////
+
+// GroupChat is a binding-layer group chat manager.
+type GroupChat struct {
+	m  *gc.Wrapper
+	id int
+}
+
+// GroupRequest is a bindings-layer interface that handles a group reception.
+//
+// Parameters:
+//  - payload - a byte serialized representation of a group.
+type GroupRequest interface {
+	Callback(payload []byte)
+}
+
+// NewManager creates a bindings-layer group chat manager.
+//
+// Parameters:
+//  - e2eID - e2e object ID in the tracker.
+//  - requestFunc - a callback to handle group chat requests.
+//  - processor - the group chat message processor.
+func NewManager(e2eID int, requestFunc GroupRequest,
+	processor GroupChatProcessor) (*GroupChat, error) {
+
+	// Get user from singleton
+	user, err := e2eTrackerSingleton.get(e2eID)
+	if err != nil {
+		return nil, err
+	}
+
+	// Construct a wrapper for the request callback
+	requestCb := func(g gs.Group) {
+		//fixme: review this to see if should be json marshaled.
+		// At the moment, groupStore.DhKeyList is an unsupported
+		// type, it would need a MarshalJson method
+		requestFunc.Callback(g.Serialize())
+	}
+
+	// Construct a group chat manager
+	gcInt, err := gc.NewManager(user.api, requestCb,
+		&groupChatProcessor{bindingsCb: processor})
+	if err != nil {
+		return nil, err
+	}
+
+	// Construct wrapper
+	return groupChatTrackerSingleton.make(gcInt), nil
+}
+
+// GetID returns the groupChatTracker ID for the GroupChat object.
+func (g *GroupChat) GetID() int {
+	return g.id
+}
+
+// MakeGroup creates a new Group and sends a group request to all members in the
+// group.
+//
+// Parameters:
+//  - membership - members the user wants in the group.
+//  - message - the initial message sent to all members in the group. This is an
+//    optional parameter and may be nil.
+//  - tag - the name of the group decided by the creator. This is an optional parameter
+//    and may be nil. If nil the group will be assigned the default name.
+//
+// Returns:
+//  - []byte - a JSON-marshalled GroupReport.
+func (g *GroupChat) MakeGroup(membership IdList, message, name []byte) (
+	[]byte, error) {
+
+	// Construct membership list into a list of []*id.Id
+	members, err := deserializeIdList(membership.Ids)
+	if err != nil {
+		return nil, err
+	}
+
+	// Construct group
+	grp, rounds, status, err := g.m.MakeGroup(members, name, message)
+	errStr := ""
+	if err != nil {
+		errStr = err.Error()
+	}
+
+	// Construct the group report
+	report := GroupReport{
+		Id:     grp.ID.Bytes(),
+		Rounds: makeRoundsList(rounds),
+		Status: int(status),
+		Err:    errStr,
+	}
+
+	// Marshal the report
+	return json.Marshal(report)
+}
+
+// ResendRequest resends a group request to all members in the group.
+//
+// Parameters:
+//  - groupId - a byte representation of a group. This can be found in the data
+//    returned by GroupChat.MakeGroup.
+//
+// Returns:
+//  - []byte - a JSON-marshalled GroupReport.
+func (g *GroupChat) ResendRequest(groupId []byte) ([]byte, error) {
+
+	// Unmarshal the group ID
+	groupID, err := id.Unmarshal(groupId)
+	if err != nil {
+		return nil,
+			errors.Errorf("Failed to unmarshal group ID: %+v", err)
+	}
+
+	// Retrieve group from manager
+	grp, exists := g.m.GetGroup(groupID)
+	if !exists {
+		return nil, errors.Errorf("Failed to find group %s", groupID)
+	}
+
+	// Resent request
+	rnds, status, err := g.m.ResendRequest(groupID)
+	errStr := ""
+	if err != nil {
+		errStr = err.Error()
+	}
+
+	// Construct the group report
+	report := &GroupReport{
+		Id:     grp.ID.Bytes(),
+		Rounds: makeRoundsList(rnds),
+		Status: int(status),
+		Err:    errStr,
+	}
+
+	// Marshal the report
+	return json.Marshal(report)
+}
+
+// JoinGroup allows a user to join a group when a request is received.
+//
+// Parameters:
+//  - group - a serialized Group. This is received by the GroupRequest.Callback.
+func (g *GroupChat) JoinGroup(group []byte) error {
+	grp, err := gs.DeserializeGroup(group)
+	if err != nil {
+		return err
+	}
+	return g.m.JoinGroup(grp)
+}
+
+// LeaveGroup deletes a group so a user no longer has access.
+//
+// Parameters:
+//  - groupId - the byte data representing a group ID.
+//    This can be pulled from a marshalled GroupReport.
+func (g *GroupChat) LeaveGroup(groupId []byte) error {
+	grpId, err := id.Unmarshal(groupId)
+	if err != nil {
+		return errors.Errorf("Failed to unmarshal group ID: %+v", err)
+	}
+
+	return g.m.LeaveGroup(grpId)
+}
+
+// Send is the bindings-level function for sending to a group.
+//
+// Parameters:
+//  - groupId - the byte data representing a group ID.
+//    This can be pulled from a marshalled GroupReport.
+//  - message - the message that the user wishes to send to the group.
+//  - tag - the tag associated with the message. This tag may be empty.
+//
+// Returns:
+//  - []byte - a JSON marshalled GroupSendReport.
+func (g *GroupChat) Send(groupId,
+	message []byte, tag string) ([]byte, error) {
+	groupID, err := id.Unmarshal(groupId)
+	if err != nil {
+		return nil, errors.Errorf("Failed to unmarshal group ID: %+v", err)
+	}
+
+	round, timestamp, msgID, err := g.m.Send(groupID, message, tag)
+	errStr := ""
+	if err != nil {
+		errStr = err.Error()
+	}
+
+	sendReport := &GroupSendReport{
+		RoundID:   round,
+		Timestamp: timestamp,
+		MessageID: msgID,
+		Err:       errStr,
+	}
+
+	return json.Marshal(sendReport)
+}
+
+// GetGroups returns an IdList containing a list of group IDs that the user is a member of.
+func (g *GroupChat) GetGroups() IdList {
+	return makeIdList(g.m.GetGroups())
+}
+
+// GetGroup returns the group with the group ID. If no group exists, then the
+// error "failed to find group" is returned.
+//
+// Parameters:
+//  - groupId - the byte data representing a group ID.
+//    This can be pulled from a marshalled GroupReport.
+// Returns:
+//  - Group - the bindings-layer representation of a Group.
+func (g *GroupChat) GetGroup(groupId []byte) (*Group, error) {
+	groupID, err := id.Unmarshal(groupId)
+	if err != nil {
+		return nil, errors.Errorf("Failed to unmarshal group ID: %+v", err)
+	}
+
+	grp, exists := g.m.GetGroup(groupID)
+	if !exists {
+		return nil, errors.New("failed to find group")
+	}
+
+	return &Group{g: grp}, nil
+}
+
+// NumGroups returns the number of groups the user is a part of.
+func (g *GroupChat) NumGroups() int {
+	return g.m.NumGroups()
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Group Structure
+////////////////////////////////////////////////////////////////////////////////
+
+// Group structure contains the identifying and membership information of a
+// group chat.
+type Group struct {
+	g  gs.Group
+	id int
+}
+
+// GetName returns the name set by the user for the group.
+func (g *Group) GetName() []byte {
+	return g.g.Name
+}
+
+// GetID return the 33-byte unique group ID.
+func (g *Group) GetID() []byte {
+	return g.g.ID.Bytes()
+}
+
+// GetInitMessage returns initial message sent with the group request.
+func (g *Group) GetInitMessage() []byte {
+	return g.g.InitMessage
+}
+
+// GetCreatedNano returns the time the group was created in nanoseconds. This is
+// also the time the group requests were sent.
+func (g *Group) GetCreatedNano() int64 {
+	return g.g.Created.UnixNano()
+}
+
+// GetCreatedMS returns the time the group was created in milliseconds. This is
+// also the time the group requests were sent.
+func (g *Group) GetCreatedMS() int64 {
+	ts := uint64(g.g.Created.UnixNano()) / uint64(time.Millisecond)
+	return int64(ts)
+}
+
+// GetMembership retrieves a list of group members. The list is in order;
+// the first contact is the leader/creator of the group.
+// All subsequent members are ordered by their ID.
+//
+// Returns:
+//  - []byte - a JSON marshalled version of the member list.
+func (g *Group) GetMembership() ([]byte, error) {
+	return json.Marshal(g.g.Members)
+}
+
+// Serialize serializes the Group.
+func (g *Group) Serialize() []byte {
+	return g.g.Serialize()
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// Group Chat Processor
+//////////////////////////////////////////////////////////////////////////////////
+
+// GroupChatProcessor manages the handling of received group chat messages.
+type GroupChatProcessor interface {
+	Process(decryptedMessage, msg, receptionId []byte, ephemeralId,
+		roundId int64, err error)
+	fmt.Stringer
+}
+
+// groupChatProcessor implements GroupChatProcessor as a way of obtaining a
+// groupChat.Processor over the bindings.
+type groupChatProcessor struct {
+	bindingsCb GroupChatProcessor
+}
+
+// convertProcessor turns the input of a groupChat.Processor to the
+// binding-layer primitives equivalents within the GroupChatProcessor.Process.
+func convertGroupChatProcessor(decryptedMsg gc.MessageReceive, msg format.Message,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) (
+	decryptedMessage, message, receptionId []byte, ephemeralId, roundId int64, err error) {
+
+	decryptedMessage, err = json.Marshal(decryptedMsg)
+	message = msg.Marshal()
+	receptionId = receptionID.Source.Marshal()
+	ephemeralId = receptionID.EphId.Int64()
+	roundId = int64(round.ID)
+	return
+}
+
+// Process handles incoming group chat messages.
+func (gcp *groupChatProcessor) Process(decryptedMsg gc.MessageReceive, msg format.Message,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+	gcp.bindingsCb.Process(convertGroupChatProcessor(decryptedMsg, msg, receptionID, round))
+}
+
+// String prints a name for debugging.
+func (gcp *groupChatProcessor) String() string {
+	return gcp.bindingsCb.String()
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// Report Structures
+////////////////////////////////////////////////////////////////////////////////
+
+// GroupReport is returned when creating a new group and contains the ID of
+// the group, a list of rounds that the group requests were sent on, and the
+// status of the send operation.
+type GroupReport struct {
+	Id     []byte
+	Rounds RoundsList
+	Status int
+	Err    string
+}
+
+// GroupSendReport is returned when sending a group message. It contains the
+// round ID sent on and the timestamp of the send operation.
+type GroupSendReport struct {
+	RoundID   id.Round
+	Timestamp time.Time
+	MessageID group.MessageID
+	Err       string
+}
diff --git a/bindings/identity.go b/bindings/identity.go
index 4af1ee4275cf5db0e469c9ed87b07a2a6ea39277..ffb4990f9b5541ff23a03decd5f0c4db84829026 100644
--- a/bindings/identity.go
+++ b/bindings/identity.go
@@ -210,3 +210,17 @@ func makeIdList(ids []*id.ID) IdList {
 	}
 	return IdList{Ids: convertedIds}
 }
+
+// deserializeIdList is a helper function which creates a list of id.ID's
+// given an IdList. It deserializes each element of the IdList using id.Unmarshal.
+func deserializeIdList(ids IdList) ([]*id.ID, error) {
+	convertedIds := make([]*id.ID, len(ids.Ids))
+	for i, serializedId := range ids.Ids {
+		deserializedId, err := id.Unmarshal(serializedId)
+		if err != nil {
+			return nil, err
+		}
+		convertedIds[i] = deserializedId
+	}
+	return convertedIds, nil
+}
diff --git a/groupChat/wrapper.go b/groupChat/wrapper.go
new file mode 100644
index 0000000000000000000000000000000000000000..6a3e91220d29cef35769f7aff86cef5899a619cc
--- /dev/null
+++ b/groupChat/wrapper.go
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package groupChat
+
+import (
+	gs "gitlab.com/elixxir/client/groupChat/groupStore"
+	"gitlab.com/elixxir/crypto/group"
+	"gitlab.com/xx_network/primitives/id"
+	"time"
+)
+
+// Wrapper handles the sending and receiving of group chat using E2E
+// messages to inform the recipient of incoming group chat messages.
+type Wrapper struct {
+	gc GroupChat
+}
+
+// NewWrapper constructs a wrapper around the GroupChat interface.
+func NewWrapper(manager GroupChat) *Wrapper {
+	return &Wrapper{gc: manager}
+}
+
+// MakeGroup calls GroupChat.MakeGroup.
+func (w *Wrapper) MakeGroup(membership []*id.ID, name, message []byte) (
+	gs.Group, []id.Round, RequestStatus, error) {
+	return w.gc.MakeGroup(membership, name, message)
+}
+
+// GetGroup calls GroupChat.GetGroup.
+func (w *Wrapper) GetGroup(groupID *id.ID) (gs.Group, bool) {
+	return w.gc.GetGroup(groupID)
+}
+
+// ResendRequest calls GroupChat.ResendRequest.
+func (w *Wrapper) ResendRequest(groupID *id.ID) ([]id.Round, RequestStatus, error) {
+	return w.gc.ResendRequest(groupID)
+}
+
+// JoinGroup calls GroupChat.JoinGroup.
+func (w *Wrapper) JoinGroup(grp gs.Group) error {
+	return w.gc.JoinGroup(grp)
+}
+
+// LeaveGroup calls GroupChat.LeaveGroup.
+func (w *Wrapper) LeaveGroup(groupID *id.ID) error {
+	return w.gc.LeaveGroup(groupID)
+}
+
+// Send calls GroupChat.Send.
+func (w *Wrapper) Send(groupID *id.ID, message []byte, tag string) (
+	id.Round, time.Time, group.MessageID, error) {
+	return w.gc.Send(groupID, tag, message)
+}
+
+// GetGroups calls GroupChat.GetGroups.
+func (w *Wrapper) GetGroups() []*id.ID {
+	return w.gc.GetGroups()
+}
+
+// NumGroups calls GroupChat.NumGroups.
+func (w *Wrapper) NumGroups() int {
+	return w.gc.NumGroups()
+}