diff --git a/clientutil/json.go b/clientutil/json.go
new file mode 100644
index 0000000000000000000000000000000000000000..862d08630a3834b7c8b094c323138f4de16dd1a0
--- /dev/null
+++ b/clientutil/json.go
@@ -0,0 +1,40 @@
+package clientutil
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+)
+
+func DoJSONHTTPRequest(client *http.Client, uri string, req, resp interface{}) error {
+	data, err := json.Marshal(req)
+	if err != nil {
+		return err
+	}
+
+	httpReq, err := http.NewRequest("POST", uri, bytes.NewReader(data))
+	if err != nil {
+		return err
+	}
+	httpReq.Header.Set("Content-Type", "application/json")
+
+	httpResp, err := RetryHTTPDo(client, httpReq, NewExponentialBackOff())
+	if err != nil {
+		return err
+	}
+	defer httpResp.Body.Close()
+
+	if httpResp.StatusCode != 200 {
+		return fmt.Errorf("HTTP status %d", httpResp.StatusCode)
+	}
+	if httpResp.Header.Get("Content-Type") != "application/json" {
+		return errors.New("not a JSON response")
+	}
+
+	if resp == nil {
+		return nil
+	}
+	return json.NewDecoder(httpResp.Body).Decode(resp)
+}