diff --git a/src/Entity/SmsMessage.php b/src/Entity/SmsMessage.php
new file mode 100644
index 0000000..ca2e32c
--- /dev/null
+++ b/src/Entity/SmsMessage.php
@@ -0,0 +1,427 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\sms\Entity\SmsMessage.
+ */
+
+namespace Drupal\sms\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\user\Entity\User;
+use Drupal\user\UserInterface;
+
+/**
+ * Defines the SMS message entity.
+ *
+ * The SMS message entity is used to maintain the message while it is queued to
+ * send to the gateway. After the message has been sent, the message may persist
+ * in the database as an archive record.
+ *
+ * @ContentEntityType(
+ *   id = "sms",
+ *   label = @Translation("SMS Message"),
+ *   base_table = "sms",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *   },
+ * )
+ */
+class SmsMessage extends ContentEntityBase implements SmsMessageInterface {
+
+  /**
+   * Following are implementors of plain SmsMessage interface.
+   *
+   * @see \Drupal\sms\Entity\SmsMessageInterface
+   */
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRecipients() {
+    $recipients = [];
+    foreach ($this->get('recipient_phone_number') as $recipient) {
+      $recipients[] = $recipient->value;
+    }
+    return $recipients;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addRecipient($recipient) {
+    $this->recipient->appendItem($recipient);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addRecipients(array $recipients) {
+    foreach ($recipients as $recipient) {
+      $this->addRecipient($recipient);
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeRecipient($recipient) {
+    $this->recipient->filter(function ($item) use ($recipient) {
+      return ($item->value != $recipient);
+    });
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeRecipients(array $recipients) {
+    $this->recipient->filter(function ($item) use ($recipients) {
+      return !in_array($item->value, $recipients);
+    });
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOptions() {
+    return ($first = $this->get('options')->first()) ? $first->getValue() : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOption($name) {
+    $options = $this->getOptions();
+    return isset($options[$name]) ? $options[$name] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOption($name, $value) {
+    $options = $this->getOptions();
+    $options[$name] = $value;
+    $this->set('options', $options);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeOption($name) {
+    $options = $this->getOptions();
+    unset($options[$name]);
+    $this->set('options', $options);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSender() {
+    $sender_name = $this->get('sender_name');
+    if (isset($sender_name)) {
+      return $sender_name;
+    } else {
+      return ($sender_entity = $this->getSenderEntity()) ? $sender_entity->label() : NULL;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param string $sender|NULL
+   *   The name of the sender. Or NULL to defer to the label of the sender
+   *   entity.
+   *
+   * @see ::getSender()
+   */
+  public function setSender($sender) {
+    $this->set('sender', $sender);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMessage() {
+    return $this->get('message')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setMessage($message) {
+    $this->set('message', $message);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUuid() {
+    return $this->get('uuid')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUid() {
+    $sender = $this->getSenderEntity();
+    return ($sender instanceof UserInterface) ? $sender->id() : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUid($uid) {
+    $this->setSenderEntity(User::load($uid));
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isAutomated() {
+    return $this->get('automated')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setAutomated($automated) {
+    $this->set('automated', $automated);
+    return $this;
+  }
+
+  /**
+   * Following are implementors of entity interface.
+   *
+   * @see \Drupal\sms\Entity\SmsMessageInterface
+   */
+
+  public function getDirection() {
+    return $this->get('direction');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getGateway() {
+    return $this->get('gateway');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setGateway($gateway) {
+    $this->set('gateway', $gateway);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSenderNumber() {
+    return $this->get('sender_phone_number')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setSenderNumber($number) {
+    $this->set('sender_phone_number', $number);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSenderEntity() {
+    return $this->get('sender_entity')->entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setSenderEntity(EntityInterface $entity) {
+    $this->set('sender_entity', $entity);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRecipientEntity() {
+    return $this->get('recipient_entity')->entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRecipientEntity(EntityInterface $entity) {
+    $this->set('recipient_entity', $entity);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQueued() {
+    return $this->get('queued')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setQueued($is_queued) {
+    $this->set('queued', $is_queued);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCreatedTime() {
+    return $this->get('created')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSendTime() {
+    return $this->get('send_on')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setSendTime($send_time) {
+    $this->set('send_on', $send_time);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProcessedTime() {
+    return $this->get('processed')->value;
+  }
+  /**
+   * {@inheritdoc}
+   */
+  public function setProcessedTime($processed) {
+    $this->set('processed', $processed);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    // Identifiers.
+    $fields['id'] = BaseFieldDefinition::create('integer')
+      ->setLabel(t('SMS message ID'))
+      ->setDescription(t('The SMS message ID.'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', TRUE);
+
+    $fields['uuid'] = BaseFieldDefinition::create('uuid')
+      ->setLabel(t('UUID'))
+      ->setDescription(t('The SMS message UUID.'))
+      ->setReadOnly(TRUE);
+
+    $fields['gateway'] = BaseFieldDefinition::create('entity_reference')
+      ->setLabel(t('Gateway Plugin'))
+      ->setDescription(t('The gateway plugin instance.'))
+      ->setSetting('target_type', 'sms_gateway')
+      ->setReadOnly(TRUE)
+      ->setRequired(TRUE);
+
+    $fields['direction'] = BaseFieldDefinition::create('integer')
+      ->setLabel(t('Transmission direction'))
+      ->setDescription(t('Transmission direction, See SmsMessageInterface::DIRECTION_*.'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', FALSE)
+      ->setSetting('size', 'tiny');
+
+    // Sender and receivers.
+    $fields['sender_name'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('Sender name'))
+      ->setDescription(t('The name of the sender.'))
+      ->setRequired(FALSE);
+
+    $fields['sender_phone_number'] = BaseFieldDefinition::create('telephone')
+      ->setLabel(t('Sender phone number'))
+      ->setDescription(t('The phone number of the sender.'))
+      ->setDefaultValue('')
+      ->setRequired(FALSE);
+
+    $fields['sender_entity'] = BaseFieldDefinition::create('dynamic_entity_reference')
+      ->setLabel(t('Sender entity'))
+      ->setDescription(t('The entity who sent the SMS message.'))
+      ->setRequired(FALSE);
+
+    $fields['recipient_phone_number'] = BaseFieldDefinition::create('telephone')
+      ->setLabel(t('Recipient phone number'))
+      ->setDescription(t('The phone number of the recipient.'))
+      ->setDefaultValue('')
+      ->setRequired(FALSE)
+      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED);
+
+    $fields['recipient_entity'] = BaseFieldDefinition::create('dynamic_entity_reference')
+      ->setLabel(t('Recipient entity'))
+      ->setDescription(t('The entity who received the SMS message.'))
+      ->setRequired(FALSE);
+
+    // Meta information.
+    $fields['options'] = BaseFieldDefinition::create('map')
+      ->setLabel(t('Options'))
+      ->setDescription(t('Options to pass to the gateway.'));
+
+    $fields['automated'] = BaseFieldDefinition::create('boolean')
+      ->setLabel(t('Is automated'))
+      ->setDescription(t('Whether this SMS message was generated automatically. 0=generated by user action, 1=generated automatically.'))
+      ->setDefaultValue(TRUE)
+      ->setRequired(TRUE);
+
+    $fields['queued'] = BaseFieldDefinition::create('boolean')
+      ->setLabel(t('Queued'))
+      ->setDescription(t('Whether the SMS message is in the queue to be processed.'))
+      ->setRequired(FALSE);
+
+    // Dates.
+    $fields['created'] = BaseFieldDefinition::create('created')
+      ->setLabel(t('Creation date'))
+      ->setDescription(t('The time the SMS message was created.'))
+      ->setRequired(TRUE);
+
+    $fields['send_on'] = BaseFieldDefinition::create('timestamp')
+      ->setLabel(t('Send date'))
+      ->setDescription(t('The time to send the SMS message.'))
+      ->setRequired(TRUE);
+
+    $fields['processed'] = BaseFieldDefinition::create('timestamp')
+      ->setLabel(t('Processed'))
+      ->setDescription(t('The time the SMS message was processed. This value does not indicate whether the message was sent, only that the gateway accepted the request.'))
+      ->setRequired(FALSE);
+
+    // Message contents.
+    $fields['message'] = BaseFieldDefinition::create('string_long')
+      ->setLabel(t('Message'))
+      ->setDescription(t('The SMS message.'))
+      ->setDefaultValue('')
+      ->setRequired(TRUE);
+
+    return $fields;
+  }
+
+}
diff --git a/src/Entity/SmsMessageInterface.php b/src/Entity/SmsMessageInterface.php
new file mode 100644
index 0000000..cd182ff
--- /dev/null
+++ b/src/Entity/SmsMessageInterface.php
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\sms\Entity\SmsMessageInterface.
+ */
+
+namespace Drupal\sms\Entity;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\sms\Message\SmsMessageInterface as PlainSmsMessageInterface;
+use Drupal\Core\Entity\EntityInterface;
+
+interface SmsMessageInterface extends ContentEntityInterface, PlainSmsMessageInterface {
+
+  /**
+   * Whether the message is queued to be sent from the website.
+   */
+  const DIRECTION_OUTGOING = 1;
+
+  /**
+   * Whether the message was received by the website.
+   */
+  const DIRECTION_INCOMING = -1;
+
+  /**
+   * Get direction of the message.
+   *
+   * @return int
+   *   See \Drupal\sms\Entity\SmsMessageInterface::DIRECTION_* constants for
+   *   potential values.
+   */
+  public function getDirection();
+
+  /**
+   * Get the gateway for this message.
+   *
+   * @return string
+   *   A gateway plugin ID.
+   */
+  public function getGateway();
+
+  /**
+   * Set the gateway for this message.
+   *
+   * @param string $gateway
+   *   A gateway plugin ID.
+   *
+   * @return $this
+   *   Return SMS message for chaining.
+   */
+  public function setGateway($gateway);
+
+  /**
+   * Get phone number of the sender.
+   *
+   * @return string
+   *   The phone number of the sender.
+   */
+  public function getSenderNumber();
+
+  /**
+   * Set the phone number of the sender.
+   *
+   * @param string $number
+   *   The phone number of the sender.
+   *
+   * @return $this
+   *   Return SMS message for chaining.
+   */
+  public function setSenderNumber($number);
+
+  /**
+   * Gets the entity who sent the SMS message.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|NULL
+   *   The entity who sent the SMS message, or NULL if it is missing.
+   */
+  public function getSenderEntity();
+
+  /**
+   * Set the entity who sent the SMS message.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity who sent the SMS message.
+   *
+   * @return $this
+   *   Return SMS message for chaining.
+   */
+  public function setSenderEntity(EntityInterface $entity);
+
+  /**
+   * Gets the entity who will receive the SMS message.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|NULL
+   *   The entity who will receive the SMS message, or NULL if it is missing.
+   */
+  public function getRecipientEntity();
+
+  /**
+   * Set the entity who will receive the SMS message.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity who will receive the SMS message.
+   *
+   * @return $this
+   *   Return SMS message for chaining.
+   */
+  public function setRecipientEntity(EntityInterface $entity);
+
+  /**
+   * Get whether the SMS message is in the queue to be processed.
+   *
+   * @return boolean
+   *   Whether the SMS message is in the queue to be processed.
+   */
+  public function getQueued();
+
+  /**
+   * Get whether the SMS message is in the queue to be processed.
+   *
+   * @param bool $is_queued
+   *   Whether the SMS message is in the queue to be processed.
+   *
+   * @return $this
+   *   Return SMS message for chaining.
+   */
+  public function setQueued($is_queued);
+
+  /**
+   * Get the creation timestamp of the SMS message.
+   *
+   * @return int
+   *   Creation timestamp of the SMS message.
+   */
+  public function getCreatedTime();
+
+  /**
+   * Get the time to send the SMS message.
+   *
+   * @return int
+   *   The timestamp after which the SMS message should be sent.
+   */
+  public function getSendTime();
+
+  /**
+   * Set the time to send the SMS message.
+   *
+   * @param int $send_time
+   *   The timestamp after which the SMS message should be sent.
+   *
+   * @return $this
+   *   Return SMS message for chaining.
+   */
+  public function setSendTime($send_time);
+
+  /**
+   * The time the SMS message was processed.
+   *
+   * This value does not indicate whether the message was sent, only that the
+   * gateway accepted the request.
+   *
+   * @return int
+   *   The timestamp when SMS message was processed.
+   */
+  public function getProcessedTime();
+
+  /**
+   * Set the time the SMS message was processed.
+   *
+   * @param int $processed
+   *   The timestamp when SMS message was processed.
+   *
+   * @return $this
+   *   Return SMS message for chaining.
+   */
+  public function setProcessedTime($processed);
+
+}
diff --git a/src/Message/SmsMessageInterface.php b/src/Message/SmsMessageInterface.php
index e77c605..e376809 100644
--- a/src/Message/SmsMessageInterface.php
+++ b/src/Message/SmsMessageInterface.php
@@ -161,7 +161,8 @@ interface SmsMessageInterface {
   /**
    * Gets the name of the sender of this SMS message.
    *
-   * @return string
+   * @return string|NULL
+   *   The name of the sender, or NULL if none is defined.
    */
   public function getSender();
 
