diff --git a/catalog/services.go b/catalog/services.go
index cd28b663e04c925eadd329a49f82aa84aec824de..24ab74a466f6499654267861760b72cbc2d6472e 100644
--- a/catalog/services.go
+++ b/catalog/services.go
@@ -23,4 +23,6 @@ const (
 	Group   = "group"
 	EndFT   = "endFT"
 	GroupRq = "groupRq"
+
+	RestLike = "restLike"
 )
diff --git a/restlike/generateProto.sh b/restlike/generateProto.sh
new file mode 100755
index 0000000000000000000000000000000000000000..67b6d293f6f4e6a68eff4162a42acb242129cd18
--- /dev/null
+++ b/restlike/generateProto.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+protoc --go_out=paths=source_relative:. restlike/restLikeMessages.proto
diff --git a/restlike/receiver.go b/restlike/receiver.go
new file mode 100644
index 0000000000000000000000000000000000000000..7a137631bea709c8b9d7dabbb0888fec60a60cdc
--- /dev/null
+++ b/restlike/receiver.go
@@ -0,0 +1,62 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"gitlab.com/elixxir/client/single"
+	"google.golang.org/protobuf/proto"
+	"time"
+)
+
+// processor is the reception handler for a RestServer
+type singleReceiver struct {
+	endpoints *Endpoints
+}
+
+// Callback is the handler for single-use message reception for a RestServer
+// Automatically responds to invalid endpoint requests
+func (s *singleReceiver) Callback(req *single.Request, receptionId receptionID.EphemeralIdentity, rounds []rounds.Round) {
+	// Unmarshal the request payload
+	newMessage := &Message{}
+	err := proto.Unmarshal(req.GetPayload(), newMessage)
+	if err != nil {
+		jww.ERROR.Printf("Unable to unmarshal restlike message: %+v", err)
+		return
+	}
+
+	var respondErr error
+	if cb, err := s.endpoints.Get(URI(newMessage.GetUri()), Method(newMessage.GetMethod())); err == nil {
+		// Send the payload to the proper Callback if it exists and respond with the result
+		respondErr = respond(cb(newMessage), req)
+	} else {
+		// If no callback, automatically send an error response
+		respondErr = respond(&Message{Error: err.Error()}, req)
+	}
+	if respondErr != nil {
+		jww.ERROR.Printf("Unable to respond to request: %+v", err)
+	}
+}
+
+// respond to a single.Request with the given Message
+func respond(response *Message, req *single.Request) error {
+	payload, err := proto.Marshal(response)
+	if err != nil {
+		return errors.Errorf("unable to marshal restlike response message: %+v", err)
+	}
+
+	// TODO: Parameterize params and timeout
+	_, err = req.Respond(payload, cmix.GetDefaultCMIXParams(), 30*time.Second)
+	if err != nil {
+		return errors.Errorf("unable to send restlike response message: %+v", err)
+	}
+	return nil
+}
diff --git a/restlike/receiver_test.go b/restlike/receiver_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..56a46586daaf6411d589b79c0e179ade2ce4e017
--- /dev/null
+++ b/restlike/receiver_test.go
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/single"
+	"testing"
+)
+
+// Test failure of proto unmarshal
+func TestSingleReceiver_Callback_FailUnmarshal(t *testing.T) {
+	ep := &Endpoints{endpoints: make(map[URI]map[Method]Callback)}
+	receiver := singleReceiver{endpoints: ep}
+
+	testReq := single.BuildTestRequest(make([]byte, 0), t)
+	receiver.Callback(testReq, receptionID.EphemeralIdentity{}, nil)
+}
+
+// Test happy path
+//func TestSingleReceiver_Callback(t *testing.T) {
+//	ep := &Endpoints{endpoints: make(map[URI]map[Method]Callback)}
+//	resultChan := make(chan interface{}, 1)
+//	cb := func(*Message) *Message {
+//		resultChan <- ""
+//		return nil
+//	}
+//	testPath := URI("test/path")
+//	testMethod := Get
+//	testMessage := &Message{
+//		Content: []byte("test"),
+//		Headers: nil,
+//		Method:  uint32(testMethod),
+//		Uri:     string(testPath),
+//		Error:   "",
+//	}
+//
+//	err := ep.Add(testPath, testMethod, cb)
+//	if err != nil {
+//		t.Errorf(err.Error())
+//	}
+//	receiver := singleReceiver{endpoints: ep}
+//
+//	testPayload, err := proto.Marshal(testMessage)
+//	if err != nil {
+//		t.Errorf(err.Error())
+//	}
+//	testReq := single.BuildTestRequest(testPayload, t)
+//	receiver.Callback(testReq, receptionID.EphemeralIdentity{}, nil)
+//
+//	select {
+//	case _ = <-resultChan:
+//	case <-time.After(3 * time.Second):
+//		t.Errorf("Test SingleReceiver timed out!")
+//	}
+//}
diff --git a/restlike/request.go b/restlike/request.go
new file mode 100644
index 0000000000000000000000000000000000000000..5b9e0427089dbeb53a4474dbb0f2afa01112e137
--- /dev/null
+++ b/restlike/request.go
@@ -0,0 +1,84 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/catalog"
+	"gitlab.com/elixxir/client/single"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/xx_network/crypto/csprng"
+	"google.golang.org/protobuf/proto"
+)
+
+// SingleRequest allows for making REST-like requests to a RestServer using single-use messages
+// Can be used as stateful or declared inline without state
+type SingleRequest struct {
+	Net    single.Cmix
+	Rng    csprng.Source
+	E2eGrp *cyclic.Group
+}
+
+// Request provides several Method of sending Data to the given URI
+// and blocks until the Message is returned
+func (s *SingleRequest) Request(method Method, recipient contact.Contact, path URI,
+	content Data, headers *Headers, singleParams single.RequestParams) (*Message, error) {
+	// Build the Message
+	newMessage := &Message{
+		Content: content,
+		Headers: headers,
+		Method:  uint32(method),
+		Uri:     string(path),
+	}
+	msg, err := proto.Marshal(newMessage)
+	if err != nil {
+		return nil, err
+	}
+
+	// Build callback for the single-use response
+	signalChannel := make(chan *Message, 1)
+	cb := func(msg *Message) {
+		signalChannel <- msg
+	}
+
+	// Transmit the Message
+	_, _, err = single.TransmitRequest(recipient, catalog.RestLike, msg,
+		&singleResponse{responseCallback: cb}, singleParams, s.Net, s.Rng, s.E2eGrp)
+	if err != nil {
+		return nil, err
+	}
+
+	// Block waiting for single-use response
+	jww.DEBUG.Printf("Restlike waiting for single-use response from %s...", recipient.ID.String())
+	newResponse := <-signalChannel
+	jww.DEBUG.Printf("Restlike single-use response received from %s", recipient.ID.String())
+
+	return newResponse, nil
+}
+
+// AsyncRequest provides several Method of sending Data to the given URI
+// and will return the Message to the given Callback when received
+func (s *SingleRequest) AsyncRequest(method Method, recipient contact.Contact, path URI,
+	content Data, headers *Headers, cb RequestCallback, singleParams single.RequestParams) error {
+	// Build the Message
+	newMessage := &Message{
+		Content: content,
+		Headers: headers,
+		Method:  uint32(method),
+		Uri:     string(path),
+	}
+	msg, err := proto.Marshal(newMessage)
+	if err != nil {
+		return err
+	}
+
+	// Transmit the Message
+	_, _, err = single.TransmitRequest(recipient, catalog.RestLike, msg,
+		&singleResponse{responseCallback: cb}, singleParams, s.Net, s.Rng, s.E2eGrp)
+	return err
+}
diff --git a/restlike/response.go b/restlike/response.go
new file mode 100644
index 0000000000000000000000000000000000000000..41e21b6061c110eaaa06fc25a472bf0326ca1ff3
--- /dev/null
+++ b/restlike/response.go
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"google.golang.org/protobuf/proto"
+)
+
+// processor is the response handler for a Request
+type singleResponse struct {
+	responseCallback RequestCallback
+}
+
+// Callback is the handler for single-use message responses for a Request
+func (s *singleResponse) Callback(payload []byte, receptionID receptionID.EphemeralIdentity, rounds []rounds.Round, err error) {
+	newMessage := &Message{}
+
+	// Handle response errors
+	if err != nil {
+		newMessage.Error = err.Error()
+		s.responseCallback(newMessage)
+		return
+	}
+
+	// Unmarshal the payload
+	err = proto.Unmarshal(payload, newMessage)
+	if err != nil {
+		newMessage.Error = err.Error()
+	}
+
+	// Send the response payload to the responseCallback
+	s.responseCallback(newMessage)
+}
diff --git a/restlike/response_test.go b/restlike/response_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0381b452cca26c9d892abfe2b8329569a1eef689
--- /dev/null
+++ b/restlike/response_test.go
@@ -0,0 +1,96 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"bytes"
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"google.golang.org/protobuf/proto"
+	"testing"
+	"time"
+)
+
+// Test happy path
+func TestSingleResponse_Callback(t *testing.T) {
+	resultChan := make(chan *Message, 1)
+	cb := func(input *Message) {
+		resultChan <- input
+	}
+	testPath := "test/path"
+	testMethod := Get
+	testMessage := &Message{
+		Content: []byte("test"),
+		Headers: nil,
+		Method:  uint32(testMethod),
+		Uri:     testPath,
+		Error:   "",
+	}
+
+	response := singleResponse{cb}
+
+	testPayload, err := proto.Marshal(testMessage)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+	response.Callback(testPayload, receptionID.EphemeralIdentity{}, nil, nil)
+
+	select {
+	case result := <-resultChan:
+		if result.Uri != testPath {
+			t.Errorf("Mismatched uri")
+		}
+		if result.Method != uint32(testMethod) {
+			t.Errorf("Mismatched method")
+		}
+		if !bytes.Equal(testMessage.Content, result.Content) {
+			t.Errorf("Mismatched content")
+		}
+	case <-time.After(3 * time.Second):
+		t.Errorf("Test SingleResponse timed out!")
+	}
+}
+
+// Test error input path
+func TestSingleResponse_Callback_Err(t *testing.T) {
+	resultChan := make(chan *Message, 1)
+	cb := func(input *Message) {
+		resultChan <- input
+	}
+	response := singleResponse{cb}
+
+	response.Callback(nil, receptionID.EphemeralIdentity{}, nil, errors.New("test"))
+
+	select {
+	case result := <-resultChan:
+		if len(result.Error) == 0 {
+			t.Errorf("Expected cb error!")
+		}
+	case <-time.After(3 * time.Second):
+		t.Errorf("Test SingleResponse input error timed out!")
+	}
+}
+
+// Test proto error path
+func TestSingleResponse_Callback_ProtoErr(t *testing.T) {
+	resultChan := make(chan *Message, 1)
+	cb := func(input *Message) {
+		resultChan <- input
+	}
+	response := singleResponse{cb}
+
+	response.Callback([]byte("test"), receptionID.EphemeralIdentity{}, nil, nil)
+
+	select {
+	case result := <-resultChan:
+		if len(result.Error) == 0 {
+			t.Errorf("Expected cb proto error!")
+		}
+	case <-time.After(3 * time.Second):
+		t.Errorf("Test SingleResponse proto error timed out!")
+	}
+}
diff --git a/restlike/restLikeMessages.pb.go b/restlike/restLikeMessages.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..c6666a5d08dd3c0a086a100a19ca0a74f630af79
--- /dev/null
+++ b/restlike/restLikeMessages.pb.go
@@ -0,0 +1,270 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.19.1
+// source: restlike/restLikeMessages.proto
+
+package restlike
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Message are used for sending to and receiving from a RestServer
+type Message struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Content []byte   `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
+	Headers *Headers `protobuf:"bytes,2,opt,name=headers,proto3" json:"headers,omitempty"`
+	Method  uint32   `protobuf:"varint,3,opt,name=method,proto3" json:"method,omitempty"`
+	Uri     string   `protobuf:"bytes,4,opt,name=uri,proto3" json:"uri,omitempty"`
+	Error   string   `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"`
+}
+
+func (x *Message) Reset() {
+	*x = Message{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_restlike_restLikeMessages_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Message) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Message) ProtoMessage() {}
+
+func (x *Message) ProtoReflect() protoreflect.Message {
+	mi := &file_restlike_restLikeMessages_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Message.ProtoReflect.Descriptor instead.
+func (*Message) Descriptor() ([]byte, []int) {
+	return file_restlike_restLikeMessages_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Message) GetContent() []byte {
+	if x != nil {
+		return x.Content
+	}
+	return nil
+}
+
+func (x *Message) GetHeaders() *Headers {
+	if x != nil {
+		return x.Headers
+	}
+	return nil
+}
+
+func (x *Message) GetMethod() uint32 {
+	if x != nil {
+		return x.Method
+	}
+	return 0
+}
+
+func (x *Message) GetUri() string {
+	if x != nil {
+		return x.Uri
+	}
+	return ""
+}
+
+func (x *Message) GetError() string {
+	if x != nil {
+		return x.Error
+	}
+	return ""
+}
+
+// Headers allows different configurations for each Request
+// that will be specified in the Request header
+type Headers struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Headers allows for custom headers to be included with a Request
+	Headers []byte `protobuf:"bytes,1,opt,name=headers,proto3" json:"headers,omitempty"`
+	// Version allows for endpoints to be backwards-compatible
+	// and handle different formats of the same Request
+	Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
+}
+
+func (x *Headers) Reset() {
+	*x = Headers{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_restlike_restLikeMessages_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Headers) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Headers) ProtoMessage() {}
+
+func (x *Headers) ProtoReflect() protoreflect.Message {
+	mi := &file_restlike_restLikeMessages_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Headers.ProtoReflect.Descriptor instead.
+func (*Headers) Descriptor() ([]byte, []int) {
+	return file_restlike_restLikeMessages_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Headers) GetHeaders() []byte {
+	if x != nil {
+		return x.Headers
+	}
+	return nil
+}
+
+func (x *Headers) GetVersion() uint32 {
+	if x != nil {
+		return x.Version
+	}
+	return 0
+}
+
+var File_restlike_restLikeMessages_proto protoreflect.FileDescriptor
+
+var file_restlike_restLikeMessages_proto_rawDesc = []byte{
+	0x0a, 0x1f, 0x72, 0x65, 0x73, 0x74, 0x6c, 0x69, 0x6b, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x4c,
+	0x69, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x12, 0x10, 0x72, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61,
+	0x67, 0x65, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
+	0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
+	0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x07, 0x68, 0x65, 0x61,
+	0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73,
+	0x74, 0x4c, 0x69, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x48, 0x65,
+	0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x16,
+	0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06,
+	0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
+	0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x3d,
+	0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61,
+	0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64,
+	0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x24, 0x5a,
+	0x22, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6c, 0x69, 0x78,
+	0x78, 0x69, 0x72, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x6c,
+	0x69, 0x6b, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_restlike_restLikeMessages_proto_rawDescOnce sync.Once
+	file_restlike_restLikeMessages_proto_rawDescData = file_restlike_restLikeMessages_proto_rawDesc
+)
+
+func file_restlike_restLikeMessages_proto_rawDescGZIP() []byte {
+	file_restlike_restLikeMessages_proto_rawDescOnce.Do(func() {
+		file_restlike_restLikeMessages_proto_rawDescData = protoimpl.X.CompressGZIP(file_restlike_restLikeMessages_proto_rawDescData)
+	})
+	return file_restlike_restLikeMessages_proto_rawDescData
+}
+
+var file_restlike_restLikeMessages_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_restlike_restLikeMessages_proto_goTypes = []interface{}{
+	(*Message)(nil), // 0: restLikeMessages.Message
+	(*Headers)(nil), // 1: restLikeMessages.Headers
+}
+var file_restlike_restLikeMessages_proto_depIdxs = []int32{
+	1, // 0: restLikeMessages.Message.headers:type_name -> restLikeMessages.Headers
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_restlike_restLikeMessages_proto_init() }
+func file_restlike_restLikeMessages_proto_init() {
+	if File_restlike_restLikeMessages_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_restlike_restLikeMessages_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Message); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_restlike_restLikeMessages_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Headers); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_restlike_restLikeMessages_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_restlike_restLikeMessages_proto_goTypes,
+		DependencyIndexes: file_restlike_restLikeMessages_proto_depIdxs,
+		MessageInfos:      file_restlike_restLikeMessages_proto_msgTypes,
+	}.Build()
+	File_restlike_restLikeMessages_proto = out.File
+	file_restlike_restLikeMessages_proto_rawDesc = nil
+	file_restlike_restLikeMessages_proto_goTypes = nil
+	file_restlike_restLikeMessages_proto_depIdxs = nil
+}
diff --git a/restlike/restLikeMessages.proto b/restlike/restLikeMessages.proto
new file mode 100644
index 0000000000000000000000000000000000000000..00156d5443904383deb89f64c391e0009741dc78
--- /dev/null
+++ b/restlike/restLikeMessages.proto
@@ -0,0 +1,30 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+syntax = "proto3";
+package restLikeMessages;
+option go_package = "gitlab.com/elixxir/client/restlike";
+
+// Message are used for sending to and receiving from a RestServer
+message Message {
+  bytes content = 1;
+  Headers headers = 2;
+  uint32 method = 3;
+  string uri = 4;
+  string error = 5;
+}
+
+// Headers allows different configurations for each Request
+// that will be specified in the Request header
+message Headers {
+  // Headers allows for custom headers to be included with a Request
+  bytes headers = 1;
+
+  // Version allows for endpoints to be backwards-compatible
+  // and handle different formats of the same Request
+  uint32 version = 2;
+}
\ No newline at end of file
diff --git a/restlike/restServer.go b/restlike/restServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..dccb13ad3da83850bdd98be9b568dfab5c933102
--- /dev/null
+++ b/restlike/restServer.go
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"gitlab.com/elixxir/client/catalog"
+	"gitlab.com/elixxir/client/single"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+// RestServer allows for clients to make REST-like requests this client
+type RestServer interface {
+	// RegisterEndpoint allows the association of a Callback with
+	// a specific URI and a variety of different REST Method
+	RegisterEndpoint(path URI, method Method, cb Callback) error
+
+	// UnregisterEndpoint removes the Callback associated with
+	// a specific URI and REST Method
+	UnregisterEndpoint(path URI, method Method) error
+
+	// Close the internal RestServer endpoints and external services
+	Close()
+}
+
+// singleServer implements the RestServer interface using single-use
+type singleServer struct {
+	receptionId *id.ID
+	listener    single.Listener
+	endpoints   *Endpoints
+}
+
+// NewSingleServer builds a RestServer with single-use and
+// the provided arguments, then registers necessary external services
+func NewSingleServer(receptionId *id.ID, privKey *cyclic.Int, net single.ListenCmix, e2eGrp *cyclic.Group) RestServer {
+	newServer := &singleServer{
+		receptionId: receptionId,
+		endpoints:   &Endpoints{endpoints: make(map[URI]map[Method]Callback)},
+	}
+	newServer.listener = single.Listen(catalog.RestLike, receptionId, privKey,
+		net, e2eGrp, &singleReceiver{newServer.endpoints})
+	return newServer
+}
+
+// RegisterEndpoint allows the association of a Callback with
+// a specific URI and a variety of different REST Method
+func (r *singleServer) RegisterEndpoint(path URI, method Method, cb Callback) error {
+	return r.endpoints.Add(path, method, cb)
+}
+
+// UnregisterEndpoint removes the Callback associated with
+// a specific URI and REST Method
+func (r *singleServer) UnregisterEndpoint(path URI, method Method) error {
+	return r.endpoints.Remove(path, method)
+}
+
+// Close the internal RestServer endpoints and external services
+func (r *singleServer) Close() {
+	// Clear all internal endpoints
+	r.endpoints = nil
+	// Destroy external services
+	r.listener.Stop()
+}
diff --git a/restlike/types.go b/restlike/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..247943844613a43c99b5d666f412a5c53f078471
--- /dev/null
+++ b/restlike/types.go
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import (
+	"github.com/pkg/errors"
+	"sync"
+)
+
+// URI defines the destination endpoint of a Request
+type URI string
+
+// Data provides a generic structure for data sent with a Request or received in a Message
+// NOTE: The way this is encoded is up to the implementation. For example, protobuf or JSON
+type Data []byte
+
+// Method defines the possible Request types
+type Method uint32
+
+// RequestCallback provides the ability to make asynchronous Request
+// in order to get the Message later without blocking
+type RequestCallback func(*Message)
+
+// Callback serves as an Endpoint function to be called when a Request is received
+// Should return the desired response to be sent back to the sender
+type Callback func(*Message) *Message
+
+const (
+	// Undefined default value
+	Undefined Method = iota
+	// Get retrieve an existing resource.
+	Get
+	// Post creates a new resource.
+	Post
+	// Put updates an existing resource.
+	Put
+	// Patch partially updates an existing resource.
+	Patch
+	// Delete a resource.
+	Delete
+)
+
+// methodStrings is a map of Method values back to their constant names for printing
+var methodStrings = map[Method]string{
+	Undefined: "undefined",
+	Get:       "get",
+	Post:      "post",
+	Put:       "put",
+	Patch:     "patch",
+	Delete:    "delete",
+}
+
+// String returns the Method as a human-readable name.
+func (m Method) String() string {
+	if methodStr, ok := methodStrings[m]; ok {
+		return methodStr
+	}
+	return methodStrings[Undefined]
+}
+
+// Endpoints represents a map of internal endpoints for a RestServer
+type Endpoints struct {
+	endpoints map[URI]map[Method]Callback
+	sync.RWMutex
+}
+
+// Add a new Endpoint
+// Returns an error if Endpoint already exists
+func (e *Endpoints) Add(path URI, method Method, cb Callback) error {
+	e.Lock()
+	defer e.Unlock()
+
+	if _, ok := e.endpoints[path]; !ok {
+		e.endpoints[path] = make(map[Method]Callback)
+	}
+	if _, ok := e.endpoints[path][method]; ok {
+		return errors.Errorf("unable to RegisterEndpoint: %s/%s already exists", path, method)
+	}
+	e.endpoints[path][method] = cb
+	return nil
+}
+
+// Get an Endpoint
+// Returns an error if Endpoint does not exist
+func (e *Endpoints) Get(path URI, method Method) (Callback, error) {
+	e.RLock()
+	defer e.RUnlock()
+
+	if _, ok := e.endpoints[path]; !ok {
+		return nil, errors.Errorf("unable to locate endpoint: %s", path)
+	}
+	if _, innerOk := e.endpoints[path][method]; !innerOk {
+		return nil, errors.Errorf("unable to locate endpoint: %s/%s", path, method)
+	}
+	return e.endpoints[path][method], nil
+}
+
+// Remove an Endpoint
+// Returns an error if Endpoint does not exist
+func (e *Endpoints) Remove(path URI, method Method) error {
+	if _, err := e.Get(path, method); err != nil {
+		return errors.Errorf("unable to UnregisterEndpoint: %s", err.Error())
+	}
+
+	e.Lock()
+	defer e.Unlock()
+	delete(e.endpoints[path], method)
+	if len(e.endpoints[path]) == 0 {
+		delete(e.endpoints, path)
+	}
+	return nil
+}
diff --git a/restlike/types_test.go b/restlike/types_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7b5b1a9d5b1bdaebc556ee4521379e48478019cb
--- /dev/null
+++ b/restlike/types_test.go
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+package restlike
+
+import "testing"
+
+// Full test for all add/get/remove cases
+func TestEndpoints(t *testing.T) {
+	ep := &Endpoints{endpoints: make(map[URI]map[Method]Callback)}
+	cb := func(*Message) *Message {
+		return nil
+	}
+
+	testPath := URI("test/path")
+	testMethod := Get
+	err := ep.Add(testPath, testMethod, cb)
+	if _, ok := ep.endpoints[testPath][testMethod]; err != nil || !ok {
+		t.Errorf("Failed to add endpoint: %+v", err)
+	}
+	err = ep.Add(testPath, testMethod, cb)
+	if _, ok := ep.endpoints[testPath][testMethod]; err == nil || !ok {
+		t.Errorf("Expected failure to add endpoint")
+	}
+
+	resultCb, err := ep.Get(testPath, testMethod)
+	if resultCb == nil || err != nil {
+		t.Errorf("Expected to get endpoint: %+v", err)
+	}
+
+	err = ep.Remove(testPath, testMethod)
+	if _, ok := ep.endpoints[testPath][testMethod]; err != nil || ok {
+		t.Errorf("Failed to remove endpoint: %+v", err)
+	}
+	err = ep.Remove(testPath, testMethod)
+	if _, ok := ep.endpoints[testPath][testMethod]; err == nil || ok {
+		t.Errorf("Expected failure to remove endpoint")
+	}
+
+	resultCb, err = ep.Get(testPath, testMethod)
+	if resultCb != nil || err == nil {
+		t.Errorf("Expected failure to get endpoint: %+v", err)
+	}
+}
diff --git a/single/receivedRequest.go b/single/receivedRequest.go
index 372557fbb0962bfcf3b9ddd4dffd8e747930b3e7..cb445e8b0a02b15d6458dd824a3cdd478d1167f3 100644
--- a/single/receivedRequest.go
+++ b/single/receivedRequest.go
@@ -18,6 +18,7 @@ import (
 	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"sync"
 	"sync/atomic"
+	"testing"
 	"time"
 )
 
@@ -223,3 +224,17 @@ func splitPayload(payload []byte, maxSize, maxParts int) [][]byte {
 	}
 	return parts
 }
+
+// BuildTestRequest can be used for mocking a Request
+func BuildTestRequest(payload []byte, t *testing.T) *Request {
+	return &Request{
+		sender:         nil,
+		senderPubKey:   nil,
+		dhKey:          nil,
+		tag:            "",
+		maxParts:       0,
+		used:           nil,
+		requestPayload: payload,
+		net:            nil,
+	}
+}