diff --git a/README.rst b/README.rst
index 7ef81dfdd3bd38283376254804ae2cfb6ead74c1..92a28b85db5125ed921c45cc14d346e9b1cedf0e 100644
--- a/README.rst
+++ b/README.rst
@@ -262,6 +262,21 @@ To enable LTI-DL and its capabilities, you need to set these settings in the blo
 
 To enable LTI-NRPS, you set **Enable LTI NRPS** to **True** in the block settings on Studio.
 
+
+LTI 1.1/1.2 Basic Outcomes Service 1.1
+--------------------------------------
+
+This XBlock supports `LTI 1.1/1.2 Basic Outcomes Service 1.0 <http://www.imsglobal.org/spec/lti-bo/v1p1/>`_. Please see these
+`LTI 1.1/1.2 Basic Outcomes Service 1.0 instructions <https://github.com/openedx/xblock-lti-consumer/tree/master/docs/basic_outcomes_service.rst>`_
+for testing the LTI 1.1/1.2 Basic Outcomes Service 1.1 implementation.
+
+LTI 2.0 Result Service 2.0
+--------------------------
+
+This XBlock supports `LTI 2.0 Result Service 2.0 <https://www.imsglobal.org/lti/model/uml/purl.imsglobal.org/vocab/lis/v2/outcomes/Result/service.html>`_.
+Please see the `LTI 2.0 Result Service 2.0 instructions <https://github.com/openedx/xblock-lti-consumer/tree/master/docs/result_service.rst>`_
+for testing the LTI 2.0 Result Service 2.0 implementation.
+
 Getting Help
 ************
 
diff --git a/docs/basic_outcomes_service.rst b/docs/basic_outcomes_service.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1c95881517cb7f049ee68f9aeed5d321830e8c9b
--- /dev/null
+++ b/docs/basic_outcomes_service.rst
@@ -0,0 +1,157 @@
+LTI 1.1/1.2 Basic Outcomes Service 1.1
+**************************************
+
+The `LTI 1.1/1.2 Basic Outcomes Service 1.1 <http://www.imsglobal.org/spec/lti-bo/v1p1/>`_ is a service that
+"supports setting, retrieving and deleting LIS [Learning Information Services] results associated with a particular
+user/resource combination."
+
+The implementation in ``xblock-lti-consumer`` currently only supports the ``replaceResult`` request type.
+
+Testing
+=======
+
+Setup
+-----
+
+* Set up an LTI 1.1/1.2 component in Studio and publish it. You can use the SaLTIre Test Tool Provider, as described in
+  the README.rst file in the root of this repository. The following instructions assume you are using the SaLTIre Test
+  Tool Provider.
+
+  * Set "Scored" to True on the LTI 1.1/1.2 component.
+
+* Set up your preferred API development and testing tool, like Postman. The following instructions assume you are using
+  Postman, but they should be generalizable to any API development and testing tool.
+    
+  * Set the request method to "POST".
+  * Set the URL to the Basic Outcomes Service URL. You can get the Basic Outcomes URL in one of two ways.
+        
+    * If you are using the SaLTIre Test Tool Provider, the Basic Outcomes Service URL will be displayed live in the
+      LTI component in the LMS under the "Message Parameters" section; it is the ``lis_outcome_service_url``.
+    * Alternatively, you can visit the LMS LTI rest endpoints view. Go to
+      ``https://<LMS_DOMAIN>/courses/<COURSE_ID>/lti_rest_endpoints/``, find your LTI component, and select the
+      ``lti_1_1_result_service_xml_endpoint``. Note that you should use ``http`` as the protocol if you are using
+      devstack.
+
+  * Set the Body type to "raw".
+  * Set the Body contents to the POX ("Plain Old XML") below. You will need to update the value of the
+    ``sourcedId`` element with the appropriate ``sourcedId``. If you are using the SaLTIre Test Tool Provider, the
+    ``sourcedId`` will be displayed live in the LTI component in the LMS under the "Message Parameters" section; it
+    is the ``lis_result_sourcedid``. You can also update the ``resultScore`` element to change the score.
+  * Set the Authorization header under "Headers" by following the directions below.
+  * Send the request.
+
+    .. code:: xml
+
+      <?xml version="1.0" encoding="UTF-8"?>
+      <imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
+      <imsx_POXHeader>
+      <imsx_POXRequestHeaderInfo>
+         <imsx_version>V1.0</imsx_version>
+         <imsx_messageIdentifier>999999123</imsx_messageIdentifier>
+      </imsx_POXRequestHeaderInfo>
+      </imsx_POXHeader>
+      <imsx_POXBody>
+      <replaceResultRequest>
+         <resultRecord>
+         <sourcedGUID>
+               <sourcedId>course-v1%3AedX%2B1717N1%2BY2022N1:localhost%3A18000-1bca781ee09347a6800ad29c346abc07:0c30252236e467a695663b9aed8d3e5d</sourcedId>
+         </sourcedGUID>
+         <result>
+               <resultScore>
+               <language>en</language>
+               <textString>0.90</textString>
+               </resultScore>
+         </result>
+         </resultRecord>
+      </replaceResultRequest>
+      </imsx_POXBody>
+      </imsx_POXEnvelopeRequest>
+
+Computing the Authorization Header
+----------------------------------
+
+* In order for a Tool Provider to send a Basic Outcomes Service request, it must do so securely. It does so by signing
+  the request using OAuth1. You will need to sign the request.
+* OAuth1 was intended to sign form-based requests, but the Basic Outcomes Service requests use an "Plain Old XML" (POX)
+  payload.
+* OAuth1 has an extension called OAuth body signing, which we use to sign a non-form-based request.
+* OAuth body signing has two steps.
+
+#. Hash the request body.
+
+   #. The OAuth1 header ``oauth_signature_method`` defines which hashing method to use.
+   #. The hashed value is stored in the OAuth1 Authorization header under the ``oauth_body_hash`` key.
+        
+#. Sign the OAuth Authorization header using the shared secret.
+
+   #. The hashed value is stored in the OAuth1 Authorization header under the ``oauth_signature`` key.
+
+Instructions
+^^^^^^^^^^^^
+
+* Open a Python shell. Install the ``oauthlib`` Python package.
+
+  * Alternatively, you can open a Django shell in the LMS or Studio devstack container with ``make lms-shell`` or
+    ``make studio-shell``, respectively. ``oauthlib`` is installed in both containers as a necessary dependency of
+    ``xblock-lti-consumer``.
+
+* Run the following commands to compute the Authorization header.
+
+  * The values for ``client_key`` and ``client_secret`` come from your LTI passport string, as described in the
+    README.rst file in the root of this repository.
+
+    .. code:: python
+
+        >>> from oauthlib import oauth1
+        >>> client_key="test"
+        >>> client_secret="secret"
+        >>> client = oauth1.Client(client_key=client_key, client_secret=client_secret)
+        >>> basic_outcomes_url = "http://localhost:18000/courses/course-v1:edX+1717N1+Y2022N1/xblock/block-v1:edX+1717N1+Y2022N1+type@lti_consumer+block@1bca781ee09347a6800ad29c346abc07/handler_noauth/outcome_service_handler"
+        >>> basic_outcomes_body = """<?xml version="1.0" encoding="UTF-8"?>
+        ... <imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
+        ... <imsx_POXHeader>
+        ...   <imsx_POXRequestHeaderInfo>
+        ...     <imsx_version>V1.0</imsx_version>
+        ...     <imsx_messageIdentifier>999999123</imsx_messageIdentifier>
+        ...   </imsx_POXRequestHeaderInfo>
+        ... </imsx_POXHeader>
+        ... <imsx_POXBody>
+        ...   <replaceResultRequest>
+        ...     <resultRecord>
+        ...       <sourcedGUID>
+        ...         <sourcedId>course-v1%3AedX%2B1717N1%2BY2022N1:localhost%3A18000-1bca781ee09347a6800ad29c346abc07:0c30252236e467a695663b9aed8d3e5d</sourcedId>
+        ...       </sourcedGUID>
+        ...       <result>
+        ...         <resultScore>
+        ...           <language>en</language>
+        ...           <textString>0.90</textString>
+        ...         </resultScore>
+        ...       </result>
+        ...     </resultRecord>
+        ...   </replaceResultRequest>
+        ... </imsx_POXBody>
+        ... </imsx_POXEnvelopeRequest>"""
+        >>> uri, headers, body = client.sign(basic_outcomes_url, http_method="POST", body=basic_outcomes_body, headers={"Content-Type": "text/xml"})
+
+* The value of ``headers`` should look something like this.
+
+  .. code:: python
+
+      {'Content-Type': 'text/xml','Authorization': 'OAuth oauth_nonce="5609288327616222561669665375", oauth_timestamp="1669665375", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="test", oauth_body_hash="vAVegN28HcixFW7OuHgfx0Ld%2Bdk%3D", oauth_signature="4Or9QJKG66jFHpZU6JeyNHcYdDk%3D"'}
+
+
+* Take the Authorization header and set the Authorization header under "Headers" to the value in your preferred API
+  development and testing tool. 
+
+Troubleshooting
+---------------
+
+* If you see the following error when trying to upload an outcome 
+  ``lti_consumer.lti_1p1.exceptions.Lti1p1Error: OAuthbody hash verification has failed``, your body hash was
+  incorrectly computed. Make sure that ``basic_outcomes_body`` matches the body you are including as data in your "POST"
+  request. A breakpoint in the Basic Outcomes Service request code can be helpful to see the value of the request body
+  as compared to the ``oauth_body_hash`` provided in the Authorization header.
+* If you’re switching between anonymous user IDs and external user IDs (i.e. toggling the 
+  ``lti_consumer.enable_external_user_id_1p1_launches`` ``CourseWaffleFlag``, you’ll need to update your XML in Postman
+  with the correct ``sourcedId`` and recompute and reset the Authorization header in your preferred API development and
+  testing tool using the instructions above.
\ No newline at end of file
diff --git a/docs/result_service.rst b/docs/result_service.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8c8fb0e77264870150e14b19316ea0e365b033a2
--- /dev/null
+++ b/docs/result_service.rst
@@ -0,0 +1,130 @@
+LTI 2.0 Result Service 2.0
+**************************
+
+The `LTI 2.0 Result Service 2.0 <https://www.imsglobal.org/lti/model/uml/purl.imsglobal.org/vocab/lis/v2/outcomes/Result/service.html>`_
+is a REST API that allows reading and updating individual Learning Information Services Result resources. See also
+`10.2 LIS Result Service <https://www.imsglobal.org/specs/ltiv2p0/implementation-guide#toc-43>`_. A Result represents
+an Outcome, or a grade, for an LTI component.
+
+The implementation in ``xblock-lti-consumer`` supports the "GET", "PUT", and "DELETE" request types.
+
+Testing
+=======
+
+Setup
+-----
+
+* Set up an LTI 1.1/1.2 component in Studio and publish it. You can use the SaLTIre Test Tool Provider, as described in
+  the README.rst file in the root of this repository. The following instructions assume you are using the SaLTIre Test
+  Tool Provider.
+
+  * Set "Scored" to True on the LTI 1.1/1.2 component.
+
+* Set up your preferred API development and testing tool, like Postman. The following instructions assume you are using
+  Postman, but they should be generalizable to any API development and testing tool.
+    
+  * Set the request method to the correct request type. The Result Service supports "GET", "PUT", and "DELETE" request
+    types.
+  * Set the URL to the Result Service URL. You can get the Result Service URL by visting the LMS LTI rest endpoints
+    view. Go to ``https://<LMS_DOMAIN>/courses/<COURSE_ID>/lti_rest_endpoints/``, find your LTI component, and select
+    the ``lti_2_0_result_service_json_endpoint``. Note that you should use ``http`` as the protocol if you are using
+    devstack. The URL will be of the form
+    ``https://<LMS_DOMAIN>>/courses/<COURSE_ID>>/xblock/<USAGE_KEY>/handler_noauth/result_service_handler/user/{anon_user_id}``.
+    You will need to substitute an appropriate value for ``anon_user_id``.
+
+    * If you are using the SaLTIre Test Tool Provider, the
+      ``sourcedId`` will be displayed live in the LTI component in the LMS under the "Message Parameters" section; it
+      is the ``lis_result_sourcedid``.
+    * If you do not have the ``lti_consumer.enable_external_user_id_1p1_launches`` ``CourseWaffleFlag`` enabled, you
+      can also find the anonymous user ID in the CSV you can download from 
+      Instructor Dashboard > Data Download > Get Student Anonymized IDs CSV.
+
+  * If you're using the "PUT" request type, set the "Content-Type" header under "Headers" to
+    ``application/vnd.ims.lis.v2.result+json``. In Postman, you must redefine the header in order to override the
+    default "Content-Type" header, which is computed automatically.
+  * Set the Body type to "raw".
+  * Set the Body contents to the JSON below. You can update the value of the "resultScore" key to change the score
+    or "comment", as needed.
+  * Set the Authorization header by following the directions below.
+  * Send the request.
+
+    .. code:: json
+
+        {
+            "@context" : "http://purl.imsglobal.org/ctx/lis/v2/Result",
+            "@type" : "Result",
+            "resultScore" : 0.83,
+            "comment" : "This is exceptional work."
+        }
+
+Computing the Authorization Header
+----------------------------------
+
+* In order for a Tool Provider to send a Basic Outcomes Service request, it must do so securely. It does so by signing
+  the request using OAuth1. You will need to sign the request.
+* OAuth1 was intended to sign form-based requests, but the Basic Outcomes Service requests use an "Plain Old XML" (POX)
+  payload.
+* OAuth1 has an extension called OAuth body signing, which we use to sign a non-form-based request.
+* OAuth body signing has two steps.
+
+#. Hash the request body.
+
+   #. The OAuth1 header ``oauth_signature_method`` defines which hashing method to use.
+   #. The hashed value is stored in the OAuth1 Authorization header under the ``oauth_body_hash`` key.
+        
+#. Sign the OAuth Authorization header using the shared secret.
+
+   #. The hashed value is stored in the OAuth1 Authorization header under the ``oauth_signature`` key.
+
+Instructions
+^^^^^^^^^^^^
+
+* Open a Python shell. Install the ``oauthlib`` Python package.
+
+  * Alternatively, you can open a Django shell in the LMS or Studio devstack container with ``make lms-shell`` or
+    ``make studio-shell``, respectively. ``oauthlib`` is installed in both containers as a necessary dependency of
+    ``xblock-lti-consumer``.
+
+* Run the following commands to compute the Authorization header. The below commands assume you are making a "PUT"
+  request. You will need to change ``json_body``, ``http_method``, and ``headers`` if you are making a "GET" or
+  "DELETE" request.
+
+  * The values for ``client_key`` and ``client_secret`` come from your LTI passport string, as described in the
+    README.rst file in the root of this repository.
+
+    .. code:: python
+
+        >>> from oauthlib import oauth1
+        >>> client_key="test"
+        >>> client_secret="secret"
+        >>> client = oauth1.Client(client_key=client_key, client_secret=client_secret)
+        >>> result_service_url = "http://localhost:18000/courses/course-v1:edX+1717N1+Y2022N1/xblock/block-v1:edX+1717N1+Y2022N1+type@lti_consumer+block@1bca781ee09347a6800ad29c346abc07/handler_noauth/result_service_handler/user/1bc0b578-6f17-4e32-917a-94dc63edddda"
+        >>> json_body = """{
+        >>>     "@context" : "http://purl.imsglobal.org/ctx/lis/v2/Result",
+        >>>     "@type" : "Result",
+        >>>     "resultScore" : 0.83,
+        >>>     "comment": "This is exceptional work."
+        >>> }"""
+        >>> uri, headers, body = client.sign(result_service_url, http_method=<"PUT">, body=json_body, headers={"Content-Type": "application/vnd.ims.lis.v2.result+json"})
+ 
+* The value of ``headers`` should look something like this.
+
+  .. code:: python
+
+      {'Content-Type': 'text/xml','Authorization': 'OAuth oauth_nonce="5609288327616222561669665375", oauth_timestamp="1669665375", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="test", oauth_body_hash="vAVegN28HcixFW7OuHgfx0Ld%2Bdk%3D", oauth_signature="4Or9QJKG66jFHpZU6JeyNHcYdDk%3D"'}
+
+* Take the Authorization header and set the Authorization header under "Headers" to the value in your preferred API
+  development and testing tool. 
+
+Troubleshooting
+---------------
+
+* If you see the following error when trying to upload an outcome 
+  ``lti_consumer.lti_1p1.exceptions.Lti1p1Error: OAuthbody hash verification has failed``, your body hash was
+  incorrectly computed. Make sure that ``result_body`` matches the body you are including as data in your "PUT"
+  request. A breakpoint in the Result Service request code can be helpful to see the value of the request body
+  as compared to the ``oauth_body_hash`` provided in the Authorization header.
+* If you’re switching between anonymous user IDs and external user IDs (i.e. toggling the 
+  ``lti_consumer.enable_external_user_id_1p1_launches`` ``CourseWaffleFlag``, you’ll need to update your URL in Postman
+  with the correct ``anon_user_id`` and recompute and reset the Authorization header in your preferred API development
+  and testing tool using the instructions above.
\ No newline at end of file