diff --git a/docs/source/tut-two-party-contract.rst b/docs/source/tut-two-party-contract.rst
new file mode 100644
index 0000000000..71f35f60cf
--- /dev/null
+++ b/docs/source/tut-two-party-contract.rst
@@ -0,0 +1,33 @@
+.. highlight:: kotlin
+.. raw:: html
+
+
+
+
+Updating the contract
+=====================
+
+Remember that each state references a contract. The contract imposes constraints on transactions involving that state.
+If the transaction does not obey the constraints of all the contracts of all its states, it cannot become a valid
+ledger update.
+
+We need to modify our contract so that the lender's signature is required in any IOU creation transaction. This will
+only require changing a single line of code. In ``IOUContract.java``/``IOUContract.kt``, update the final line of the
+``requireThat`` block as follows:
+
+.. container:: codeset
+
+ .. code-block:: kotlin
+
+ "The borrower and lender must be signers." using (command.signers.containsAll(listOf(
+ out.borrower.owningKey, out.lender.owningKey)))
+
+ .. code-block:: java
+
+ check.using("The borrower and lender must be signers.", command.getSigners().containsAll(
+ ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
+
+Progress so far
+---------------
+Our contract now imposes an additional constraint - the lender must also sign an IOU creation transaction. Next, we
+need to update ``IOUFlow`` so that it actually gathers the counterparty's signature as part of the flow.
\ No newline at end of file
diff --git a/docs/source/tut-two-party-flow.rst b/docs/source/tut-two-party-flow.rst
new file mode 100644
index 0000000000..00a2658441
--- /dev/null
+++ b/docs/source/tut-two-party-flow.rst
@@ -0,0 +1,203 @@
+.. highlight:: kotlin
+.. raw:: html
+
+
+
+
+Updating the flow
+=================
+
+To update the flow, we'll need to do two things:
+
+* Update the borrower's side of the flow to request the lender's signature
+* Create a flow for the lender to run in response to a signature request from the borrower
+
+Updating the borrower's flow
+----------------------------
+In the original CorDapp, we automated the process of notarising a transaction and recording it in every party's vault
+by invoking a built-in flow called ``FinalityFlow`` as a subflow. We're going to use another pre-defined flow, called
+``CollectSignaturesFlow``, to gather the lender's signature.
+
+We also need to add the lender's public key to the transaction's command, making the lender one of the required signers
+on the transaction.
+
+In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
+
+.. container:: codeset
+
+ .. code-block:: kotlin
+
+ // We add the items to the builder.
+ val state = IOUState(iouValue, me, otherParty)
+ val cmd = Command(IOUContract.Create(), listOf(me.owningKey, otherParty.owningKey))
+ txBuilder.withItems(state, cmd)
+
+ // Verifying the transaction.
+ txBuilder.verify(serviceHub)
+
+ // Signing the transaction.
+ val signedTx = serviceHub.signInitialTransaction(txBuilder)
+
+ // Obtaining the counterparty's signature
+ val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx))
+
+ // Finalising the transaction.
+ subFlow(FinalityFlow(fullySignedTx))
+
+ .. code-block:: java
+
+ // We add the items to the builder.
+ IOUState state = new IOUState(iouValue, me, otherParty);
+ List requiredSigners = ImmutableList.of(me.getOwningKey(), otherParty.getOwningKey());
+ Command cmd = new Command(new IOUContract.Create(), requiredSigners);
+ txBuilder.withItems(state, cmd);
+
+ // Verifying the transaction.
+ txBuilder.verify(getServiceHub());
+
+ // Signing the transaction.
+ final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
+
+ // Obtaining the counterparty's signature
+ final SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(signedTx, null));
+
+ // Finalising the transaction.
+ subFlow(new FinalityFlow(fullySignedTx));
+
+To make the lender a required signer, we simply add the lender's public key to the list of signers on the command.
+
+``CollectSignaturesFlow``, meanwhile, takes a transaction signed by the flow initiator, and returns a transaction
+signed by all the transaction's other required signers. We then pass this fully-signed transaction into
+``FinalityFlow``.
+
+The lender's flow
+-----------------
+Reorganising our class
+^^^^^^^^^^^^^^^^^^^^^^
+Before we define the lender's flow, let's reorganise ``IOUFlow.java``/``IOUFlow.kt`` a little bit:
+
+* Rename ``IOUFlow`` to ``Initiator``
+* In Java, make the ``Initiator`` class static, rename its constructor to match the new name, and move the definition
+ inside an enclosing ``IOUFlow`` class
+* In Kotlin, move the definition of ``Initiator`` class inside an enclosing ``IOUFlow`` singleton object
+
+We will end up with the following structure:
+
+.. container:: codeset
+
+ .. code-block:: kotlin
+
+ object IOUFlow {
+ @InitiatingFlow
+ @StartableByRPC
+ class Initiator(val iouValue: Int,
+ val otherParty: Party) : FlowLogic() {
+
+ .. code-block:: java
+
+ public class IOUFlow {
+ @InitiatingFlow
+ @StartableByRPC
+ public static class Initiator extends FlowLogic {
+
+Writing the lender's flow
+^^^^^^^^^^^^^^^^^^^^^^^^^
+We're now ready to write the lender's flow, which will respond to the borrower's attempt to gather our signature.
+
+Inside the ``IOUFlow`` class/singleton object, add the following class:
+
+.. container:: codeset
+
+ .. code-block:: kotlin
+
+ @InitiatedBy(Initiator::class)
+ class Acceptor(val otherParty: Party) : FlowLogic() {
+ @Suspendable
+ override fun call() {
+ val signTransactionFlow = object : SignTransactionFlow(otherParty) {
+ override fun checkTransaction(stx: SignedTransaction) = requireThat {
+ val output = stx.tx.outputs.single().data
+ "This must be an IOU transaction." using (output is IOUState)
+ val iou = output as IOUState
+ "The IOU's value can't be too high." using (iou.value < 100)
+ }
+ }
+
+ subFlow(signTransactionFlow)
+ }
+ }
+
+ .. code-block:: java
+
+ @InitiatedBy(Initiator.class)
+ public static class Acceptor extends FlowLogic {
+
+ private final Party otherParty;
+
+ public Acceptor(Party otherParty) {
+ this.otherParty = otherParty;
+ }
+
+ @Suspendable
+ @Override
+ public Void call() throws FlowException {
+ class signTxFlow extends SignTransactionFlow {
+ private signTxFlow(Party otherParty) {
+ super(otherParty, null);
+ }
+
+ @Override
+ protected void checkTransaction(SignedTransaction stx) {
+ requireThat(require -> {
+ ContractState output = stx.getTx().getOutputs().get(0).getData();
+ require.using("This must be an IOU transaction.", output instanceof IOUState);
+ IOUState iou = (IOUState) output;
+ require.using("The IOU's value can't be too high.", iou.getValue() < 100);
+ return null;
+ });
+ }
+ }
+
+ subFlow(new signTxFlow(otherParty));
+
+ return null;
+ }
+ }
+
+As with the ``Initiator``, our ``Acceptor`` flow is a ``FlowLogic`` subclass where we've overridden ``FlowLogic.call``.
+
+The flow is annotated with ``InitiatedBy(Initiator.class)``, which means that your node will invoke ``Acceptor.call``
+when it receives a message from a instance of ``Initiator`` running on another node. What will this message from the
+``Initiator`` be? If we look at the definition of ``CollectSignaturesFlow``, we can see that we'll be sent a
+``SignedTransaction``, and are expected to send back our signature over that transaction.
+
+We could handle this manually. However, there is also a pre-defined flow called ``SignTransactionFlow`` that can handle
+this process for us automatically. ``SignTransactionFlow`` is an abstract class, and we must subclass it and override
+``SignTransactionFlow.checkTransaction``.
+
+Once we've defined the subclass, we invoke it using ``FlowLogic.subFlow``, and the communication with the borrower's
+and the lender's flow is conducted automatically.
+
+CheckTransactions
+~~~~~~~~~~~~~~~~~
+``SignTransactionFlow`` will automatically verify the transaction and its signatures before signing it. However, just
+because a transaction is valid doesn't mean we necessarily want to sign. What if we don't want to deal with the
+counterparty in question, or the value is too high, or we're not happy with the transaction's structure?
+
+Overriding ``SignTransactionFlow.checkTransaction`` allows us to define these additional checks. In our case, we are
+checking that:
+
+* The transaction involves an ``IOUState`` - this ensures that ``IOUContract`` will be run to verify the transaction
+* The IOU's value is less than some amount (100 in this case)
+
+If either of these conditions are not met, we will not sign the transaction - even if the transaction and its
+signatures are valid.
+
+Conclusion
+----------
+We have now updated our flow to gather the lender's signature as well, in line with the constraints in ``IOUContract``.
+We can now run our updated CorDapp, using the instructions :doc:`here `.
+
+Our CorDapp now requires agreement from both the lender and the borrower before an IOU can be created on the ledger.
+This prevents either the lender or the borrower from unilaterally updating the ledger in a way that only benefits
+themselves.
diff --git a/docs/source/tut-two-party-index.rst b/docs/source/tut-two-party-index.rst
new file mode 100644
index 0000000000..27c69f3e33
--- /dev/null
+++ b/docs/source/tut-two-party-index.rst
@@ -0,0 +1,10 @@
+Two-party flows
+===============
+
+.. toctree::
+ :maxdepth: 1
+
+ tut-two-party-introduction
+ tut-two-party-contract
+ tut-two-party-flow
+ tut-two-party-running
\ No newline at end of file
diff --git a/docs/source/tut-two-party-introduction.rst b/docs/source/tut-two-party-introduction.rst
new file mode 100644
index 0000000000..46f924583f
--- /dev/null
+++ b/docs/source/tut-two-party-introduction.rst
@@ -0,0 +1,25 @@
+Introduction
+============
+
+.. note:: This tutorial extends the CorDapp built during the :doc:`Hello, World tutorial `. You can
+ find the final version of the CorDapp produced in that tutorial
+ `here `_.
+
+In the Hello, World tutorial, we built a CorDapp allowing us to model IOUs on ledger. Our CorDapp was made up of three
+elements:
+
+* An ``IOUState``, representing IOUs on the ledger
+* An ``IOUContract``, controlling the evolution of IOUs over time
+* An ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
+
+However, in our original CorDapp, only the IOU's borrower was required to sign transactions issuing IOUs. The lender
+had no say in whether the issuance of the IOU was a valid ledger update or not.
+
+In this tutorial, we'll update our code so that the borrower requires the lender's agreement before they can issue an
+IOU onto the ledger. We'll need to make two changes:
+
+* The ``IOUContract`` will need to be updated so that transactions involving an ``IOUState`` will require the lender's
+ signature (as well as the borrower's) to become valid ledger updates
+* The ``IOUFlow`` will need to be updated to allow for the gathering of the lender's signature
+
+We'll start by updating the contract.
\ No newline at end of file
diff --git a/docs/source/tut-two-party-running.rst b/docs/source/tut-two-party-running.rst
new file mode 100644
index 0000000000..a616769fda
--- /dev/null
+++ b/docs/source/tut-two-party-running.rst
@@ -0,0 +1,28 @@
+Running our CorDapp
+===================
+
+
+
+Conclusion
+----------
+We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Like all CorDapps, our
+CorDapp is made up of three key parts:
+
+* The ``IOUState``, representing IOUs on the ledger
+* The ``IOUContract``, controlling the evolution of IOUs over time
+* The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger.
+
+Together, these three parts completely determine how IOUs are created and evolved on the ledger.
+
+Next steps
+----------
+You should now be ready to develop your own CorDapps. There's
+`a more fleshed-out version of the IOU CorDapp `_
+with an API and web front-end, and a set of example CorDapps in
+`the main Corda repo `_, under ``samples``. An explanation of how to run these
+samples :doc:`here `.
+
+As you write CorDapps, you can learn more about the API available :doc:`here `.
+
+If you get stuck at any point, please reach out on `Slack `_,
+`Discourse `_, or `Stack Overflow `_.
\ No newline at end of file
diff --git a/docs/source/tutorials-index.rst b/docs/source/tutorials-index.rst
index bd92064680..faaa498335 100644
--- a/docs/source/tutorials-index.rst
+++ b/docs/source/tutorials-index.rst
@@ -5,6 +5,7 @@ Tutorials
:maxdepth: 1
hello-world-index
+ tut-two-party-index
tutorial-contract
tutorial-contract-clauses
tutorial-test-dsl