diff --git a/bindings/single.go b/bindings/single.go
new file mode 100644
index 0000000000000000000000000000000000000000..23ae6b6ddd32967acf0551cda0e87d38b3b8a6d6
--- /dev/null
+++ b/bindings/single.go
@@ -0,0 +1,151 @@
+package bindings
+
+import (
+	"encoding/json"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"gitlab.com/elixxir/client/single"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+// JSON Types
+
+// SingleUseSendReport is the bindings struct used to represent information returned by single.TransmitRequest
+//
+// Example json marshalled struct:
+// {"Rounds":[1,5,9],
+//  "EphID":{"EphId":[0,0,0,0,0,0,3,89],
+//  "Source":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"}}
+type SingleUseSendReport struct {
+	RoundsList
+	EphID receptionID.EphemeralIdentity
+}
+
+// SingleUseResponseReport is the bindings struct used to represent information passed
+// to the single.Response callback interface in response to single.TransmitRequest
+//
+// Example json marshalled struct:
+// {"Rounds":[1,5,9],
+//  "Payload":"rSuPD35ELWwm5KTR9ViKIz/r1YGRgXIl5792SF8o8piZzN6sT4Liq4rUU/nfOPvQEjbfWNh/NYxdJ72VctDnWw==",
+//  "ReceptionID":{"EphId":[0,0,0,0,0,0,3,89],
+//  "Source":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"},
+//  "Err":null}
+type SingleUseResponseReport struct {
+	RoundsList
+	Payload     []byte
+	ReceptionID receptionID.EphemeralIdentity
+	Err         error
+}
+
+// SingleUseCallbackReport is the bindings struct used to represent single use messages
+// received by a callback passed into single.Listen
+//
+// Example json marshalled struct:
+// {"Rounds":[1,5,9],
+//  "Payload":"rSuPD35ELWwm5KTR9ViKIz/r1YGRgXIl5792SF8o8piZzN6sT4Liq4rUU/nfOPvQEjbfWNh/NYxdJ72VctDnWw==",
+//  "Partner":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD",
+//  "EphID":{"EphId":[0,0,0,0,0,0,3,89],
+//  "Source":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"}}
+type SingleUseCallbackReport struct {
+	RoundsList
+	Payload []byte
+	Partner *id.ID
+	EphID   receptionID.EphemeralIdentity
+}
+
+// Function types
+
+// StopFunc is the function to stop a listener returned to the bindings layer when one is started
+type StopFunc func()
+
+// SingleUseCallback func is passed into Listen and called when messages are received
+// Accepts a SingleUseCallbackReport marshalled to json
+type SingleUseCallback func(callbackReport []byte, err error)
+
+// SingleUseResponse is the public facing callback func passed by bindings clients into TransmitSingleUse
+// Accepts a SingleUseResponseReport marshalled to json
+type SingleUseResponse func(responseReport []byte, err error)
+
+/* CALLBACK WRAPPERS */
+
+/* listener struct */
+
+// singleUseListener is the internal struct used to wrap a SingleUseCallback func,
+// which matches the single.Receiver interface
+type singleUseListener struct {
+	scb SingleUseCallback
+}
+
+// Callback is called whenever a single use message is heard by the listener, and translates the info to
+//a SingleUseCallbackReport which is marshalled & passed to bindings
+func (sl singleUseListener) Callback(req *single.Request, eid receptionID.EphemeralIdentity, rl []rounds.Round) {
+	var rids []id.Round
+	for _, r := range rl {
+		rids = append(rids, r.ID)
+	}
+
+	// Todo: what other info from req needs to get to bindings
+	scr := SingleUseCallbackReport{
+		Payload:    req.GetPayload(),
+		RoundsList: makeRoundsList(rids),
+		Partner:    req.GetPartner(),
+		EphID:      eid,
+	}
+
+	sl.scb(json.Marshal(scr))
+}
+
+/* response struct */
+
+// singleUseResponse is the private struct backing SingleUseResponse, which subscribes to the single.Response interface
+type singleUseResponse struct {
+	responseFunc SingleUseResponse
+}
+
+// Callback builds a SingleUseSendReport & passes the json marshalled version into the callback
+func (sr singleUseResponse) Callback(payload []byte, receptionID receptionID.EphemeralIdentity,
+	rounds []rounds.Round, err error) {
+	var rids []id.Round
+	for _, r := range rounds {
+		rids = append(rids, r.ID)
+	}
+	sendReport := SingleUseResponseReport{
+		RoundsList:  makeRoundsList(rids),
+		ReceptionID: receptionID,
+		Payload:     payload,
+		Err:         err,
+	}
+	sr.responseFunc(json.Marshal(&sendReport))
+}
+
+/* PUBLIC WRAPPER METHODS */
+
+// TransmitSingleUse accepts a marshalled recipient contact object, tag, payload, SingleUseResponse callback func & a
+// Client.  Transmits payload to recipient via single use
+func TransmitSingleUse(recipient []byte, tag string, payload []byte, responseCB SingleUseResponse, cl *Client) ([]byte, error) {
+	recipientContact, err := contact.Unmarshal(recipient)
+	if err != nil {
+		return nil, err
+	}
+
+	rcb := &singleUseResponse{responseFunc: responseCB}
+
+	rids, eid, err := single.TransmitRequest(recipientContact, tag, payload, rcb, single.GetDefaultRequestParams(), cl.api.GetCmix(), cl.api.GetRng().GetStream(), cl.api.GetStorage().GetE2EGroup())
+
+	if err != nil {
+		return nil, err
+	}
+	sr := SingleUseSendReport{
+		EphID:      eid,
+		RoundsList: makeRoundsList(rids),
+	}
+	return json.Marshal(sr)
+}
+
+// Listen starts a single use listener on a given tag using the passed in client and SingleUseCallback func
+func Listen(tag string, cl *Client, cb SingleUseCallback) StopFunc {
+	listener := singleUseListener{scb: cb}
+	l := single.Listen(tag, cl.api.GetUser().ReceptionID, cl.api.GetUser().E2eDhPrivateKey, cl.api.GetCmix(), cl.api.GetStorage().GetE2EGroup(), listener)
+	return l.Stop
+}
diff --git a/bindings/single_test.go b/bindings/single_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec64d2e17ca610595c2681d723faaf60c159ec50
--- /dev/null
+++ b/bindings/single_test.go
@@ -0,0 +1,64 @@
+package bindings
+
+import (
+	"encoding/json"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"testing"
+	"time"
+)
+
+func TestSingleUseJsonMarshals(t *testing.T) {
+	rids := []id.Round{1, 5, 9}
+	rl := makeRoundsList(rids)
+	rid := id.NewIdFromString("zezima", id.User, t)
+	eid, _, _, err := ephemeral.GetId(rid, 16, time.Now().UnixNano())
+	if err != nil {
+		t.Fatalf("Failed to generate ephemeral id: %+v", err)
+	}
+	ephId := receptionID.EphemeralIdentity{
+		EphId:  eid,
+		Source: rid,
+	}
+	payload := make([]byte, 64)
+	rng := csprng.NewSystemRNG()
+	rng.Read(payload)
+	sendReport := SingleUseSendReport{
+		RoundsList: rl,
+		EphID:      ephId,
+	}
+	srm, err := json.Marshal(sendReport)
+	if err != nil {
+		t.Errorf("Failed to marshal send report to JSON: %+v", err)
+	} else {
+		t.Logf("Marshalled send report:\n%s\n", string(srm))
+	}
+
+	responseReport := SingleUseResponseReport{
+		RoundsList:  rl,
+		Payload:     payload,
+		ReceptionID: ephId,
+		Err:         nil,
+	}
+	rrm, err := json.Marshal(responseReport)
+	if err != nil {
+		t.Errorf("Failed to marshal response report to JSON: %+v", err)
+	} else {
+		t.Logf("Marshalled response report:\n%s\n", string(rrm))
+	}
+
+	callbackReport := SingleUseCallbackReport{
+		RoundsList: rl,
+		Payload:    payload,
+		Partner:    rid,
+		EphID:      ephId,
+	}
+	crm, err := json.Marshal(callbackReport)
+	if err != nil {
+		t.Errorf("Failed to marshal callback report to JSON: %+v", err)
+	} else {
+		t.Logf("Marshalled callback report:\n%s\n", string(crm))
+	}
+}