diff --git a/id/id.go b/id/id.go
index 6739f1e39f8558a0669aabc61fe40d8dfce60221..8914f87eed12d4329fe6e75f1c54e202ecbdaf12 100644
--- a/id/id.go
+++ b/id/id.go
@@ -15,6 +15,7 @@ import (
 	"encoding/base64"
 	"encoding/binary"
 	"encoding/hex"
+	"encoding/json"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"io"
@@ -112,6 +113,30 @@ func (id *ID) SetType(idType Type) {
 	id[ArrIDLen-1] = byte(idType)
 }
 
+// UnmarshalJSON is part of the json.Unmarshaler interface and allows IDs to be
+// marshaled into JSON.
+func (id *ID) UnmarshalJSON(b []byte) error {
+	var buff []byte
+	if err := json.Unmarshal(b, &buff); err != nil {
+		return err
+	}
+
+	newID, err := Unmarshal(buff)
+	if err != nil {
+		return err
+	}
+
+	*id = *newID
+
+	return nil
+}
+
+// MarshalJSON is part of the json.Marshaler interface and allows IDs to be
+// marshaled into JSON.
+func (id ID) MarshalJSON() ([]byte, error) {
+	return json.Marshal(id.Marshal())
+}
+
 // NewRandomID generates a random ID using the passed in io.Reader r
 // and sets the ID to Type t. If the base64 string of the generated
 // ID does not begin with an alphanumeric character, then another ID
diff --git a/id/id_test.go b/id/id_test.go
index 71cb079b8932b3014a13b20bcecb9527b8c2a6f7..5d48d23c97ca48065cdd1d36a6a53f17e388fdc1 100644
--- a/id/id_test.go
+++ b/id/id_test.go
@@ -10,6 +10,7 @@ import (
 	"bytes"
 	"encoding/base64"
 	"encoding/binary"
+	"encoding/json"
 	"fmt"
 	"math/rand"
 	"reflect"
@@ -301,6 +302,50 @@ func TestID_SetType_NilError(t *testing.T) {
 	id.SetType(Generic)
 }
 
+// Tests that an ID can be JSON marshaled and unmarshalled.
+func TestID_MarshalJSON_UnmarshalJSON(t *testing.T) {
+	testID := NewIdFromBytes(rngBytes(ArrIDLen, 42, t), t)
+
+	jsonData, err := json.Marshal(testID)
+	if err != nil {
+		t.Errorf("json.Marshal returned an error: %+v", err)
+	}
+
+	newID := &ID{}
+	err = json.Unmarshal(jsonData, newID)
+	if err != nil {
+		t.Errorf("json.Unmarshal returned an error: %+v", err)
+	}
+
+	if *testID != *newID {
+		t.Errorf("Failed the JSON marshal and unmarshal ID."+
+			"\noriginal ID: %s\nreceived ID: %s", testID, newID)
+	}
+}
+
+// Error path: supplied data is invalid JSON.
+func TestID_UnmarshalJSON_JsonUnmarshalError(t *testing.T) {
+	expectedErr := "invalid character"
+	id := ID{}
+	err := id.UnmarshalJSON([]byte("invalid JSON"))
+	if err == nil || !strings.Contains(err.Error(), expectedErr) {
+		t.Errorf("UnmarshalJSON failed to return the expected error for "+
+			"invalid JSON.\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Error path: supplied data is valid JSON but an invalid ID.
+func TestID_UnmarshalJSON_IdUnmarshalError(t *testing.T) {
+	expectedErr := fmt.Sprintf("Failed to unmarshal ID: length of data "+
+		"must be %d, length received is %d", ArrIDLen, 0)
+	id := ID{}
+	err := id.UnmarshalJSON([]byte("\"\""))
+	if err == nil || !strings.Contains(err.Error(), expectedErr) {
+		t.Errorf("UnmarshalJSON failed to return the expected error for "+
+			"invalid ID.\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
 // Tests that NewRandomID returns the expected IDs for a given PRNG.
 func TestNewRandomID_Consistency(t *testing.T) {
 	prng := rand.New(rand.NewSource(42))
@@ -599,3 +644,19 @@ func (prng *alphaNumericPRNG) Read(p []byte) (n int, err error) {
 		return len(p), nil
 	}
 }
+
+// Generates a byte slice of the specified length containing random numbers.
+func rngBytes(length int, seed int64, t *testing.T) []byte {
+	prng := rand.New(rand.NewSource(seed))
+
+	// Create new byte slice of the correct size
+	idBytes := make([]byte, length)
+
+	// Create random bytes
+	_, err := prng.Read(idBytes)
+	if err != nil {
+		t.Fatalf("Failed to generate random bytes: %+v", err)
+	}
+
+	return idBytes
+}