diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
index 75af1b87ca73cfe6e04a61506b709f282bee37e7..c9002bb0e61fb3f8f2989a1fdf308e52afa6858c 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
@@ -3,12 +3,14 @@ package org.briarproject.briar.introduction;
 import org.briarproject.bramble.api.FormatException;
 import org.briarproject.bramble.api.client.ClientHelper;
 import org.briarproject.bramble.api.client.ContactGroupFactory;
+import org.briarproject.bramble.api.contact.Contact;
 import org.briarproject.bramble.api.contact.ContactManager;
 import org.briarproject.bramble.api.data.BdfDictionary;
 import org.briarproject.bramble.api.db.DatabaseComponent;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.Transaction;
 import org.briarproject.bramble.api.identity.Author;
+import org.briarproject.bramble.api.identity.AuthorId;
 import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.plugin.TransportId;
@@ -18,6 +20,8 @@ import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.briar.api.client.MessageTracker;
 import org.briarproject.briar.api.client.SessionId;
+import org.briarproject.briar.api.introduction.IntroductionResponse;
+import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
 
 import java.util.Map;
 
@@ -141,6 +145,21 @@ abstract class AbstractProtocolEngine<S extends Session>
 		}
 	}
 
+	void broadcastIntroductionResponseReceivedEvent(Transaction txn,
+			Session s, AuthorId sender, AbstractIntroductionMessage m)
+			throws DbException {
+		AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
+		Contact c = contactManager.getContact(txn, sender, localAuthorId);
+		IntroductionResponse response =
+				new IntroductionResponse(s.getSessionId(), m.getMessageId(),
+						m.getGroupId(), s.getRole(), m.getTimestamp(), false,
+						false, false, false, c.getAuthor().getName(),
+						m instanceof AcceptMessage);
+		IntroductionResponseReceivedEvent e =
+				new IntroductionResponseReceivedEvent(c.getId(), response);
+		txn.attach(e);
+	}
+
 	void markMessageVisibleInUi(Transaction txn, MessageId m)
 			throws DbException {
 		BdfDictionary meta = new BdfDictionary();
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
index ce7d5f76c2c45f2268aa9624f7c8336ab790c76c..df0a719005c2b6fb632f73370b3e1f23f577b0d7 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
@@ -27,10 +27,8 @@ import org.briarproject.briar.api.client.MessageTracker;
 import org.briarproject.briar.api.client.ProtocolStateException;
 import org.briarproject.briar.api.client.SessionId;
 import org.briarproject.briar.api.introduction.IntroductionRequest;
-import org.briarproject.briar.api.introduction.IntroductionResponse;
 import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
 import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent;
-import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
 import org.briarproject.briar.api.introduction.event.IntroductionSucceededEvent;
 
 import java.security.GeneralSecurityException;
@@ -44,7 +42,9 @@ import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
 import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_AUTH;
 import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_RESPONSES;
 import static org.briarproject.briar.introduction.IntroduceeState.LOCAL_ACCEPTED;
+import static org.briarproject.briar.introduction.IntroduceeState.LOCAL_DECLINED;
 import static org.briarproject.briar.introduction.IntroduceeState.REMOTE_ACCEPTED;
+import static org.briarproject.briar.introduction.IntroduceeState.START;
 
 @Immutable
 @NotNullByDefault
@@ -142,12 +142,12 @@ class IntroduceeProtocolEngine
 	public IntroduceeSession onAcceptMessage(Transaction txn,
 			IntroduceeSession session, AcceptMessage m) throws DbException {
 		switch (session.getState()) {
-			case START:
-				return onRemoteResponseInStart(txn, session, m);
+			case LOCAL_DECLINED:
+				return onRemoteResponseWhenDeclined(txn, session, m);
 			case AWAIT_RESPONSES:
 			case LOCAL_ACCEPTED:
 				return onRemoteAccept(txn, session, m);
-			case LOCAL_DECLINED:
+			case START:
 			case REMOTE_ACCEPTED:
 			case AWAIT_AUTH:
 			case AWAIT_ACTIVATE:
@@ -161,12 +161,12 @@ class IntroduceeProtocolEngine
 	public IntroduceeSession onDeclineMessage(Transaction txn,
 			IntroduceeSession session, DeclineMessage m) throws DbException {
 		switch (session.getState()) {
-			case START:
-				return onRemoteResponseInStart(txn, session, m);
-			case AWAIT_RESPONSES:
 			case LOCAL_DECLINED:
+				return onRemoteResponseWhenDeclined(txn, session, m);
+			case AWAIT_RESPONSES:
 			case LOCAL_ACCEPTED:
 				return onRemoteDecline(txn, session, m);
+			case START:
 			case REMOTE_ACCEPTED:
 			case AWAIT_AUTH:
 			case AWAIT_ACTIVATE:
@@ -255,8 +255,7 @@ class IntroduceeProtocolEngine
 	}
 
 	private IntroduceeSession onLocalAccept(Transaction txn,
-			IntroduceeSession s, long timestamp)
-			throws DbException {
+			IntroduceeSession s, long timestamp) throws DbException {
 		// Mark the request message unavailable to answer
 		markRequestsUnavailableToAnswer(txn, s);
 
@@ -291,20 +290,23 @@ class IntroduceeProtocolEngine
 	}
 
 	private IntroduceeSession onLocalDecline(Transaction txn,
-			IntroduceeSession s, long timestamp)
-			throws DbException {
+			IntroduceeSession s, long timestamp) throws DbException {
 		// Mark the request message unavailable to answer
 		markRequestsUnavailableToAnswer(txn, s);
 
 		// Send a DECLINE message
 		long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s));
 		Message sent = sendDeclineMessage(txn, s, localTimestamp, true);
+
 		// Track the message
 		messageTracker.trackOutgoingMessage(txn, sent);
 
-		// Move to the START state
-		return IntroduceeSession.clear(s, sent.getId(), sent.getTimestamp(),
-				s.getLastRemoteMessageId());
+		// Move to the START or LOCAL_DECLINED state, if still awaiting response
+		IntroduceeState state =
+				s.getState() == REMOTE_ACCEPTED ? START : LOCAL_DECLINED;
+		return IntroduceeSession
+				.clear(s, state, sent.getId(), sent.getTimestamp(),
+						s.getLastRemoteMessageId());
 	}
 
 	private IntroduceeSession onRemoteAccept(Transaction txn,
@@ -347,25 +349,17 @@ class IntroduceeProtocolEngine
 				.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
 
 		// Broadcast IntroductionResponseReceivedEvent
-		Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
-				identityManager.getLocalAuthor(txn).getId());
-		IntroductionResponse request =
-				new IntroductionResponse(s.getSessionId(), m.getMessageId(),
-						m.getGroupId(), INTRODUCEE, m.getTimestamp(), false,
-						false, false, false, s.getRemote().author.getName(),
-						false);
-		IntroductionResponseReceivedEvent e =
-				new IntroductionResponseReceivedEvent(c.getId(), request);
-		txn.attach(e);
+		broadcastIntroductionResponseReceivedEvent(txn, s,
+				s.getIntroducer().getId(), m);
 
 		// Move back to START state
-		return IntroduceeSession
-				.clear(s, s.getLastLocalMessageId(), s.getLocalTimestamp(),
-						m.getMessageId());
+		return IntroduceeSession.clear(s, START, s.getLastLocalMessageId(),
+				s.getLocalTimestamp(), m.getMessageId());
 	}
 
-	private IntroduceeSession onRemoteResponseInStart(Transaction txn,
-			IntroduceeSession s, AbstractIntroductionMessage m) throws DbException {
+	private IntroduceeSession onRemoteResponseWhenDeclined(Transaction txn,
+			IntroduceeSession s, AbstractIntroductionMessage m)
+			throws DbException {
 		// The timestamp must be higher than the last request message
 		if (m.getTimestamp() <= s.getRequestTimestamp())
 			return abort(txn, s);
@@ -373,10 +367,9 @@ class IntroduceeProtocolEngine
 		if (isInvalidDependency(s, m.getPreviousMessageId()))
 			return abort(txn, s);
 
-		// Stay in START state
-		return IntroduceeSession
-				.clear(s, s.getLastLocalMessageId(), s.getLocalTimestamp(),
-						m.getMessageId());
+		// Move to START state
+		return IntroduceeSession.clear(s, START, s.getLastLocalMessageId(),
+				s.getLocalTimestamp(), m.getMessageId());
 	}
 
 	private IntroduceeSession onLocalAuth(Transaction txn, IntroduceeSession s)
@@ -479,9 +472,8 @@ class IntroduceeProtocolEngine
 		keyManager.activateKeys(txn, s.getTransportKeys());
 
 		// Move back to START state
-		return IntroduceeSession
-				.clear(s, s.getLastLocalMessageId(), s.getLocalTimestamp(),
-						m.getMessageId());
+		return IntroduceeSession.clear(s, START, s.getLastLocalMessageId(),
+				s.getLocalTimestamp(), m.getMessageId());
 	}
 
 	private IntroduceeSession onRemoteAbort(Transaction txn,
@@ -494,9 +486,8 @@ class IntroduceeProtocolEngine
 		txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
 
 		// Reset the session back to initial state
-		return IntroduceeSession
-				.clear(s, s.getLastLocalMessageId(), s.getLocalTimestamp(),
-						m.getMessageId());
+		return IntroduceeSession.clear(s, START, s.getLastLocalMessageId(),
+				s.getLocalTimestamp(), m.getMessageId());
 	}
 
 	private IntroduceeSession abort(Transaction txn, IntroduceeSession s)
@@ -511,8 +502,9 @@ class IntroduceeProtocolEngine
 		txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
 
 		// Reset the session back to initial state
-		return IntroduceeSession.clear(s, sent.getId(), sent.getTimestamp(),
-				s.getLastRemoteMessageId());
+		return IntroduceeSession
+				.clear(s, START, sent.getId(), sent.getTimestamp(),
+						s.getLastRemoteMessageId());
 	}
 
 	private boolean isInvalidDependency(IntroduceeSession s,
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java
index 92b06abc05a36f5f50c714850a9dfe74befe2c33..b952b3dc52e9c64a5c8900cb6e5c27131e309cc5 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java
@@ -121,7 +121,7 @@ class IntroduceeSession extends Session<IntroduceeState>
 				remote, null, transportKeys);
 	}
 
-	static IntroduceeSession clear(IntroduceeSession s,
+	static IntroduceeSession clear(IntroduceeSession s, IntroduceeState state,
 			@Nullable MessageId lastLocalMessageId, long localTimestamp,
 			@Nullable MessageId lastRemoteMessageId) {
 		Local local =
@@ -130,7 +130,7 @@ class IntroduceeSession extends Session<IntroduceeState>
 		Remote remote =
 				new Remote(s.remote.alice, s.remote.author, lastRemoteMessageId,
 						null, null, -1, null);
-		return new IntroduceeSession(s.getSessionId(), START,
+		return new IntroduceeSession(s.getSessionId(), state,
 				s.getRequestTimestamp(), s.contactGroupId, s.introducer, local,
 				remote, null, null);
 	}
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
index 7115ec089232ddfda5cdd56f25b3aa540c67713a..8aeb28277a8be9ec59a18cc736bbb9160b8eaf55 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
@@ -2,12 +2,11 @@ package org.briarproject.briar.introduction;
 
 import org.briarproject.bramble.api.client.ClientHelper;
 import org.briarproject.bramble.api.client.ContactGroupFactory;
-import org.briarproject.bramble.api.contact.Contact;
 import org.briarproject.bramble.api.contact.ContactManager;
 import org.briarproject.bramble.api.db.DatabaseComponent;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.Transaction;
-import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.sync.GroupId;
@@ -16,16 +15,13 @@ import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.briar.api.client.MessageTracker;
 import org.briarproject.briar.api.client.ProtocolStateException;
-import org.briarproject.briar.api.introduction.IntroductionResponse;
 import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
-import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
 import org.briarproject.briar.introduction.IntroducerSession.Introducee;
 
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 
-import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
 import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATES;
 import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_A;
 import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_B;
@@ -35,6 +31,8 @@ import static org.briarproject.briar.introduction.IntroducerState.AWAIT_AUTH_B;
 import static org.briarproject.briar.introduction.IntroducerState.AWAIT_RESPONSES;
 import static org.briarproject.briar.introduction.IntroducerState.AWAIT_RESPONSE_A;
 import static org.briarproject.briar.introduction.IntroducerState.AWAIT_RESPONSE_B;
+import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED;
+import static org.briarproject.briar.introduction.IntroducerState.B_DECLINED;
 import static org.briarproject.briar.introduction.IntroducerState.START;
 
 @Immutable
@@ -68,6 +66,8 @@ class IntroducerProtocolEngine
 			case AWAIT_RESPONSES:
 			case AWAIT_RESPONSE_A:
 			case AWAIT_RESPONSE_B:
+			case A_DECLINED:
+			case B_DECLINED:
 			case AWAIT_AUTHS:
 			case AWAIT_AUTH_A:
 			case AWAIT_AUTH_B:
@@ -111,8 +111,10 @@ class IntroducerProtocolEngine
 			case AWAIT_RESPONSE_A:
 			case AWAIT_RESPONSE_B:
 				return onRemoteAccept(txn, s, m);
+			case A_DECLINED:
+			case B_DECLINED:
+				return onRemoteResponseWhenDeclined(txn, s, m);
 			case START:
-				return onRemoteResponseInStart(txn, s, m);
 			case AWAIT_AUTHS:
 			case AWAIT_AUTH_A:
 			case AWAIT_AUTH_B:
@@ -133,8 +135,10 @@ class IntroducerProtocolEngine
 			case AWAIT_RESPONSE_A:
 			case AWAIT_RESPONSE_B:
 				return onRemoteDecline(txn, s, m);
+			case A_DECLINED:
+			case B_DECLINED:
+				return onRemoteResponseWhenDeclined(txn, s, m);
 			case START:
-				return onRemoteResponseInStart(txn, s, m);
 			case AWAIT_AUTHS:
 			case AWAIT_AUTH_A:
 			case AWAIT_AUTH_B:
@@ -159,6 +163,8 @@ class IntroducerProtocolEngine
 			case AWAIT_RESPONSES:
 			case AWAIT_RESPONSE_A:
 			case AWAIT_RESPONSE_B:
+			case A_DECLINED:
+			case B_DECLINED:
 			case AWAIT_ACTIVATES:
 			case AWAIT_ACTIVATE_A:
 			case AWAIT_ACTIVATE_B:
@@ -180,6 +186,8 @@ class IntroducerProtocolEngine
 			case AWAIT_RESPONSES:
 			case AWAIT_RESPONSE_A:
 			case AWAIT_RESPONSE_B:
+			case A_DECLINED:
+			case B_DECLINED:
 			case AWAIT_AUTHS:
 			case AWAIT_AUTH_A:
 			case AWAIT_AUTH_B:
@@ -262,17 +270,8 @@ class IntroducerProtocolEngine
 		}
 
 		// Broadcast IntroductionResponseReceivedEvent
-		AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
-		Contact c = contactManager.getContact(txn,
-				senderIsAlice ? introduceeA.author.getId() :
-						introduceeB.author.getId(), localAuthorId);
-		IntroductionResponse request =
-				new IntroductionResponse(s.getSessionId(), m.getMessageId(),
-						m.getGroupId(), INTRODUCER, m.getTimestamp(), false,
-						false, false, false, c.getAuthor().getName(), true);
-		IntroductionResponseReceivedEvent e =
-				new IntroductionResponseReceivedEvent(c.getId(), request);
-		txn.attach(e);
+		Author sender = senderIsAlice ? introduceeA.author : introduceeB.author;
+		broadcastIntroductionResponseReceivedEvent(txn, s, sender.getId(), m);
 
 		// Move to the next state
 		return new IntroducerSession(s.getSessionId(), state,
@@ -312,34 +311,28 @@ class IntroducerProtocolEngine
 		long timestamp = getLocalTimestamp(s, i);
 		Message sent = sendDeclineMessage(txn, i, timestamp, false);
 
-		// Update introducee state
+		// Create the next state
+		IntroducerState state = START;
 		Introducee introduceeA, introduceeB;
 		if (senderIsAlice) {
+			if (s.getState() == AWAIT_RESPONSES) state = A_DECLINED;
 			introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
 			introduceeB = new Introducee(s.getIntroduceeB(), sent);
 		} else {
+			if (s.getState() == AWAIT_RESPONSES) state = B_DECLINED;
 			introduceeA = new Introducee(s.getIntroduceeA(), sent);
 			introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
 		}
 
 		// Broadcast IntroductionResponseReceivedEvent
-		AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
-		Contact c = contactManager.getContact(txn,
-				senderIsAlice ? introduceeA.author.getId() :
-						introduceeB.author.getId(), localAuthorId);
-		IntroductionResponse request =
-				new IntroductionResponse(s.getSessionId(), m.getMessageId(),
-						m.getGroupId(), INTRODUCER, m.getTimestamp(), false,
-						false, false, false, c.getAuthor().getName(), false);
-		IntroductionResponseReceivedEvent e =
-				new IntroductionResponseReceivedEvent(c.getId(), request);
-		txn.attach(e);
+		Author sender = senderIsAlice ? introduceeA.author : introduceeB.author;
+		broadcastIntroductionResponseReceivedEvent(txn, s, sender.getId(), m);
 
-		return new IntroducerSession(s.getSessionId(), START,
+		return new IntroducerSession(s.getSessionId(), state,
 				s.getRequestTimestamp(), introduceeA, introduceeB);
 	}
 
-	private IntroducerSession onRemoteResponseInStart(Transaction txn,
+	private IntroducerSession onRemoteResponseWhenDeclined(Transaction txn,
 			IntroducerSession s, AbstractIntroductionMessage m)
 			throws DbException {
 		// The timestamp must be higher than the last request message
@@ -355,33 +348,19 @@ class IntroducerProtocolEngine
 		messageTracker
 				.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
 
-		Introducee i = getIntroducee(s, m.getGroupId());
+		boolean senderIsAlice = senderIsAlice(s, m);
 		Introducee introduceeA, introduceeB;
-		AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
-		Contact c;
-		if (i.equals(s.getIntroduceeA())) {
+		if (senderIsAlice) {
 			introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
 			introduceeB = s.getIntroduceeB();
-			c = contactManager
-					.getContact(txn, s.getIntroduceeA().author.getId(),
-							localAuthorId);
-		} else if (i.equals(s.getIntroduceeB())) {
+		} else {
 			introduceeA = s.getIntroduceeA();
 			introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
-			c = contactManager
-					.getContact(txn, s.getIntroduceeB().author.getId(),
-							localAuthorId);
-		} else throw new AssertionError();
+		}
 
 		// Broadcast IntroductionResponseReceivedEvent
-		IntroductionResponse request =
-				new IntroductionResponse(s.getSessionId(), m.getMessageId(),
-						m.getGroupId(), INTRODUCER, m.getTimestamp(), false,
-						false, false, false, c.getAuthor().getName(),
-						m instanceof AcceptMessage);
-		IntroductionResponseReceivedEvent e =
-				new IntroductionResponseReceivedEvent(c.getId(), request);
-		txn.attach(e);
+		Author sender = senderIsAlice ? introduceeA.author : introduceeB.author;
+		broadcastIntroductionResponseReceivedEvent(txn, s, sender.getId(), m);
 
 		return new IntroducerSession(s.getSessionId(), START,
 				s.getRequestTimestamp(), introduceeA, introduceeB);
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerState.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerState.java
index 99c3fbf86c755ed80104ab6e2748a187342ccec5..6514eca16feb6cfd511f8558d2c26cf8e7096742 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerState.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerState.java
@@ -12,10 +12,11 @@ enum IntroducerState implements State {
 	START(0),
 	AWAIT_RESPONSES(1),
 	AWAIT_RESPONSE_A(2), AWAIT_RESPONSE_B(3),
-	AWAIT_AUTHS(4),
-	AWAIT_AUTH_A(5), AWAIT_AUTH_B(6),
-	AWAIT_ACTIVATES(7),
-	AWAIT_ACTIVATE_A(8), AWAIT_ACTIVATE_B(9);
+	A_DECLINED(4), B_DECLINED(5),
+	AWAIT_AUTHS(6),
+	AWAIT_AUTH_A(7), AWAIT_AUTH_B(8),
+	AWAIT_ACTIVATES(9),
+	AWAIT_ACTIVATE_A(10), AWAIT_ACTIVATE_B(11);
 
 	private final int value;
 
diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java
index 3dccbbc8dec17248f29b337b176263c7b6be2fd6..3c602a2e3f318c0f9595eb4fd71898508cc5bb78 100644
--- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java
@@ -20,7 +20,6 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
 import org.briarproject.bramble.api.properties.TransportProperties;
 import org.briarproject.bramble.api.properties.TransportPropertyManager;
 import org.briarproject.bramble.api.sync.Group;
-import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.bramble.api.sync.Message;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.test.TestDatabaseModule;
@@ -52,6 +51,10 @@ import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
 import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
 import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
 import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
+import static org.briarproject.briar.introduction.IntroduceeState.LOCAL_DECLINED;
+import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED;
+import static org.briarproject.briar.introduction.IntroducerState.B_DECLINED;
+import static org.briarproject.briar.introduction.IntroducerState.START;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
 import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_AUTHOR;
 import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_A;
@@ -171,8 +174,7 @@ public class IntroductionIntegrationTest
 		sync0To2(1, true);
 
 		// assert that introducee2 did add the transport keys
-		IntroduceeSession session2 = getIntroduceeSession(c2.getClientHelper(),
-				introductionManager2.getContactGroup(contact0From2).getId());
+		IntroduceeSession session2 = getIntroduceeSession(c2);
 		assertNotNull(session2.getTransportKeys());
 		assertFalse(session2.getTransportKeys().isEmpty());
 
@@ -181,8 +183,7 @@ public class IntroductionIntegrationTest
 		sync0To1(2, true);
 
 		// assert that introducee1 really purged the key material
-		IntroduceeSession session1 = getIntroduceeSession(c1.getClientHelper(),
-				introductionManager1.getContactGroup(contact0From1).getId());
+		IntroduceeSession session1 = getIntroduceeSession(c1);
 		assertNull(session1.getMasterKey());
 		assertNull(session1.getLocal().ephemeralPrivateKey);
 		assertNull(session1.getTransportKeys());
@@ -240,16 +241,32 @@ public class IntroductionIntegrationTest
 		assertTrue(listener1.requestReceived);
 		assertTrue(listener2.requestReceived);
 
+		// assert that introducee is in correct state
+		IntroduceeSession introduceeSession = getIntroduceeSession(c1);
+		assertEquals(LOCAL_DECLINED, introduceeSession.getState());
+
 		// sync first response
 		sync1To0(1, true);
 		eventWaiter.await(TIMEOUT, 1);
 		assertTrue(listener0.response1Received);
 
+		// assert that introducer is in correct state
+		boolean alice = c0.getIntroductionCrypto()
+				.isAlice(introducee1.getAuthor().getId(),
+						introducee2.getAuthor().getId());
+		IntroducerSession introducerSession = getIntroducerSession();
+		assertEquals(alice ? A_DECLINED : B_DECLINED,
+				introducerSession.getState());
+
 		// sync second response
 		sync2To0(1, true);
 		eventWaiter.await(TIMEOUT, 1);
 		assertTrue(listener0.response2Received);
 
+		// assert that introducer now moved to START state
+		introducerSession = getIntroducerSession();
+		assertEquals(START, introducerSession.getState());
+
 		// sync first forwarded response
 		sync0To2(1, true);
 
@@ -1114,13 +1131,17 @@ public class IntroductionIntegrationTest
 		return c0.getSessionParser().parseIntroducerSession(d);
 	}
 
-	private IntroduceeSession getIntroduceeSession(ClientHelper ch,
-			GroupId introducerGroup) throws DbException, FormatException {
-		Map<MessageId, BdfDictionary> dicts =
-				ch.getMessageMetadataAsDictionary(getLocalGroup().getId());
+	private IntroduceeSession getIntroduceeSession(
+			IntroductionIntegrationTestComponent c)
+			throws DbException, FormatException {
+		Map<MessageId, BdfDictionary> dicts = c.getClientHelper()
+				.getMessageMetadataAsDictionary(getLocalGroup().getId());
 		assertEquals(1, dicts.size());
 		BdfDictionary d = dicts.values().iterator().next();
-		return c0.getSessionParser().parseIntroduceeSession(introducerGroup, d);
+		Group introducerGroup =
+				introductionManager2.getContactGroup(contact0From2);
+		return c.getSessionParser()
+				.parseIntroduceeSession(introducerGroup.getId(), d);
 	}
 
 	private Group getLocalGroup() {
diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java
index b8d5dedaa47b69093f7cfef9c358f849fdf5b4c8..afd6d4394a696e738a1d61982794954f16bfe729 100644
--- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java
+++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java
@@ -62,5 +62,6 @@ interface IntroductionIntegrationTestComponent
 	MessageEncoder getMessageEncoder();
 	MessageParser getMessageParser();
 	SessionParser getSessionParser();
+	IntroductionCrypto getIntroductionCrypto();
 
 }