diff --git a/src/Messages/Interactions/TSErrorMessage.m b/src/Messages/Interactions/TSErrorMessage.m
index a15ab1057..5fff70653 100644
--- a/src/Messages/Interactions/TSErrorMessage.m
+++ b/src/Messages/Interactions/TSErrorMessage.m
@@ -98,7 +98,8 @@ NS_ASSUME_NONNULL_BEGIN
             break;
         }
         case TSErrorMessageUnknownContactBlockOffer:
-            return NSLocalizedString(@"UNKNOWN_CONTACT_BLOCK_OFFER", nil);
+            return NSLocalizedString(@"UNKNOWN_CONTACT_BLOCK_OFFER",
+                @"Message shown in conversation view that offers to block an unknown user.");
         default:
             return NSLocalizedString(@"ERROR_MESSAGE_UNKNOWN_ERROR", @"");
             break;
diff --git a/src/Messages/Interactions/TSInfoMessage.h b/src/Messages/Interactions/TSInfoMessage.h
index d4f631e46..9ff08ed40 100644
--- a/src/Messages/Interactions/TSInfoMessage.h
+++ b/src/Messages/Interactions/TSInfoMessage.h
@@ -14,7 +14,8 @@ typedef NS_ENUM(NSInteger, TSInfoMessageType) {
     TSInfoMessageTypeUnsupportedMessage,
     TSInfoMessageTypeGroupUpdate,
     TSInfoMessageTypeGroupQuit,
-    TSInfoMessageTypeDisappearingMessagesUpdate
+    TSInfoMessageTypeDisappearingMessagesUpdate,
+    TSInfoMessageAddToContactsOffer,
 };
 
 + (instancetype)userNotRegisteredMessageInThread:(TSThread *)thread;
diff --git a/src/Messages/Interactions/TSInfoMessage.m b/src/Messages/Interactions/TSInfoMessage.m
index 9411aa409..31464fb7d 100644
--- a/src/Messages/Interactions/TSInfoMessage.m
+++ b/src/Messages/Interactions/TSInfoMessage.m
@@ -64,6 +64,9 @@ NS_ASSUME_NONNULL_BEGIN
             return NSLocalizedString(@"GROUP_YOU_LEFT", nil);
         case TSInfoMessageTypeGroupUpdate:
             return _customMessage != nil ? _customMessage : NSLocalizedString(@"GROUP_UPDATED", nil);
+        case TSInfoMessageAddToContactsOffer:
+            return NSLocalizedString(@"ADD_TO_CONTACTS_OFFER",
+                @"Message shown in conversation view that offers to add an unknown user to your phone's contacts.");
         default:
             break;
     }
diff --git a/src/Messages/OWSAddToContactsOfferMessage.h b/src/Messages/OWSAddToContactsOfferMessage.h
new file mode 100644
index 000000000..aec3bc036
--- /dev/null
+++ b/src/Messages/OWSAddToContactsOfferMessage.h
@@ -0,0 +1,17 @@
+//
+//  Copyright (c) 2017 Open Whisper Systems. All rights reserved.
+//
+
+#import "TSInfoMessage.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface OWSAddToContactsOfferMessage : TSInfoMessage
+
++ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId;
+
+@property (nonatomic, readonly) NSString *contactId;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/Messages/OWSAddToContactsOfferMessage.m b/src/Messages/OWSAddToContactsOfferMessage.m
new file mode 100644
index 000000000..b870f00ca
--- /dev/null
+++ b/src/Messages/OWSAddToContactsOfferMessage.m
@@ -0,0 +1,47 @@
+//
+//  Copyright (c) 2017 Open Whisper Systems. All rights reserved.
+//
+
+#import "OWSAddToContactsOfferMessage.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface OWSAddToContactsOfferMessage ()
+
+@property (nonatomic) NSString *contactId;
+
+@end
+
+#pragma mark -
+
+@implementation OWSAddToContactsOfferMessage
+
++ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId
+{
+    return [[OWSAddToContactsOfferMessage alloc] initWithTimestamp:timestamp thread:thread contactId:contactId];
+}
+
+- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId
+{
+    self = [super initWithTimestamp:timestamp inThread:thread messageType:TSInfoMessageAddToContactsOffer];
+
+    if (self) {
+        _contactId = contactId;
+    }
+
+    return self;
+}
+
+- (nullable NSDate *)receiptDateForSorting
+{
+    // Always use date, since we're creating these interactions after the fact
+    // and back-dating them.
+    //
+    // By default [TSMessage receiptDateForSorting] will prefer to use receivedAtDate
+    // which is not back-dated.
+    return self.date;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END