diff --git a/include/axolotl/account.hh b/include/axolotl/account.hh
new file mode 100644
index 0000000000000000000000000000000000000000..5edb799709a8802c11c7d2a89f3162e40aac8fe6
--- /dev/null
+++ b/include/axolotl/account.hh
@@ -0,0 +1,57 @@
+#ifndef AXOLOTL_ACCOUNT_HH_
+#define AXOLOTL_ACCOUNT_HH_
+
+#include "axolotl/list.hh"
+
+#include <cstdint>
+
+namespace axolotl {
+
+
+struct LocalKey {
+    std::uint32_t id;
+    Curve25519KeyPair key;
+};
+
+
+struct SignedKey : LocalKey {
+    std::uint8_t signature[64];
+};
+
+
+static std::size_t const MAX_ONE_TIME_KEYS = 100;
+
+struct Account {
+    LocalKey identity_key;
+    LocalKey last_resort_one_time_key;
+    List<LocalKey, MAX_ONE_TIME_KEYS> one_time_keys;
+
+    /** Number of random bytes needed to create a new account */
+    std::size_t new_account_random_length();
+
+    /** Create a new account. Returns NOT_ENOUGH_RANDOM if the number of random
+     * bytes is too small. */
+    ErrorCode new_account(
+        uint8_t const * random, std::size_t random_length
+    );
+
+    /** The number of bytes needed to persist this account. */
+    std::size_t pickle_length();
+
+    /** Persists an account as a sequence of bytes
+     * Returns the number of output bytes used. */
+    std::size_t pickle(
+        std::uint8_t * output, std::size_t output_length
+    );
+
+    /** Loads an account from a sequence of bytes.
+     * Returns 0 on success, or std::size_t(-1) on failure. */
+    std::size_t unpickle(
+        std::uint8_t * input, std::size_t input_length
+    );
+};
+
+
+} // namespace axolotl
+
+#endif /* AXOLOTL_ACCOUNT_HH_ */
diff --git a/include/axolotl/error.hh b/include/axolotl/error.hh
new file mode 100644
index 0000000000000000000000000000000000000000..712b9eb5e520d9ca38a93cf75648d508ad8f304e
--- /dev/null
+++ b/include/axolotl/error.hh
@@ -0,0 +1,17 @@
+#ifndef ERROR_HH_
+#define ERROR_HH_
+
+namespace axolotl {
+
+enum struct ErrorCode {
+    SUCCESS = 0, /*!< There wasn't an error */
+    NOT_ENOUGH_RANDOM = 1,  /*!< Not enough entropy was supplied */
+    OUTPUT_BUFFER_TOO_SMALL = 2, /*!< Supplied output buffer is too small */
+    BAD_MESSAGE_VERSION = 3,  /*!< The message version is unsupported */
+    BAD_MESSAGE_FORMAT = 4, /*!< The message couldn't be decoded */
+    BAD_MESSAGE_MAC = 5, /*!< The message couldn't be decrypted */
+};
+
+} // namespace axolotl
+
+#endif /* ERROR_HH_ */
diff --git a/include/axolotl/ratchet.hh b/include/axolotl/ratchet.hh
index d5053b20b9d63c7dd9f6995f6861590cb674d555..f7d30d0b2ad7b3e300f84dc7a92a581e36a3983c 100644
--- a/include/axolotl/ratchet.hh
+++ b/include/axolotl/ratchet.hh
@@ -15,6 +15,7 @@
 
 #include "axolotl/crypto.hh"
 #include "axolotl/list.hh"
+#include "axolotl/error.hh"
 
 namespace axolotl {
 
@@ -53,16 +54,6 @@ struct SkippedMessageKey {
 };
 
 
-enum struct ErrorCode {
-    SUCCESS = 0, /*!< There wasn't an error */
-    NOT_ENOUGH_RANDOM = 1,  /*!< Not enough entropy was supplied */
-    OUTPUT_BUFFER_TOO_SMALL = 2, /*!< Supplied output buffer is too small */
-    BAD_MESSAGE_VERSION = 3,  /*!< The message version is unsupported */
-    BAD_MESSAGE_FORMAT = 4, /*!< The message couldn't be decoded */
-    BAD_MESSAGE_MAC = 5, /*!< The message couldn't be decrypted */
-};
-
-
 static std::size_t const MAX_RECEIVER_CHAINS = 5;
 static std::size_t const MAX_SKIPPED_MESSAGE_KEYS = 40;
 
@@ -124,12 +115,12 @@ struct Ratchet {
     );
 
     /** The number of bytes needed to persist the current session. */
-    std::size_t pickle_max_output_length();
+    std::size_t pickle_length();
 
     /** Persists a session as a sequence of bytes
      * Returns the number of output bytes used. */
     std::size_t pickle(
-        std::uint8_t * output, std::size_t max_output_length
+        std::uint8_t * output, std::size_t output_length
     );
 
     /** Loads a session from a sequence of bytes.
diff --git a/include/axolotl/session.hh b/include/axolotl/session.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c69699d42cbe02de19bb9e7078fce0f782e18cb9
--- /dev/null
+++ b/include/axolotl/session.hh
@@ -0,0 +1,78 @@
+#ifndef AXOLOTL_SESSION_HH_
+#define AXOLOTL_SESSION_HH_
+
+#include "axolotl/ratchet.hh"
+
+namespace axolotl {
+
+struct RemoteKey {
+    std::uint32_t id;
+    Curve25519PublicKey key;
+};
+
+struct RemoteKeys {
+};
+
+
+enum struct MessageType {
+    PRE_KEY_MESSAGE = 0,
+    MESSAGE = 1,
+};
+
+
+struct Session {
+    bool received_message;
+    RemoteKey alice_identity_key;
+    RemoteKey alice_base_key;
+    RemoteKey bob_identity_key;
+    RemoteKey bob_one_time_key;
+    Ratchet ratchet;
+
+    void initialise_outbound_session_random_length();
+
+    void initialise_outbound_session(
+        Account const & local_account,
+        RemoteKey const & identity_key,
+        RemoteKey const & one_time_key,
+        std::uint8_t const * random, std::size_t random_length
+    );
+
+    void initialise_inbound_session(
+        Account & local_account,
+        std::uint8_t const * one_time_key_message, std::size_t message_length
+    );
+
+    void matches_inbound_session(
+        std::uint8_t const * one_time_key_message, std::size_t message_length
+    );
+
+    MessageType encrypt_message_type();
+
+    std::size_t encrypt_message_length(
+        std::size_t plaintext_length
+    );
+
+    std::size_t encrypt_random_length();
+
+    std::size_t encrypt(
+        std::uint8_t const * plaintext, std::size_t plaintext_length,
+        std::uint8_t const * random, std::size_t random_length,
+        std::uint8_t * message, std::size_t message_length
+    );
+
+    std::size_t decrypt_max_plaintext_length(
+        MessageType message_type,
+        std::uint8_t const * message, std::size_t message_length
+    );
+
+    std::size_t decrypt(
+        MessageType message_type,
+        std::uint8_t const * message, std::size_t message_length,
+        std::uint8_t * plaintext, std::size_t max_plaintext_length
+    );
+};
+
+
+} // namespace axolotl
+
+#endif /* AXOLOTL_SESSION_HH_ */
diff --git a/src/ratchet.cpp b/src/ratchet.cpp
index bdd0ba3e1efe764bf00fed2dd2cf6e935ec906c3..509764330ac98c153718f6d8e8a19d041fd97a91 100644
--- a/src/ratchet.cpp
+++ b/src/ratchet.cpp
@@ -215,11 +215,11 @@ void axolotl::Ratchet::initialise_as_alice(
 }
 
 
-std::size_t axolotl::Ratchet::pickle_max_output_length() {
+std::size_t axolotl::Ratchet::pickle_length() {
     std::size_t counter_length = 4;
     std::size_t send_chain_length = counter_length + 64 + 32;
     std::size_t recv_chain_length = counter_length + 32 + 32;
-    std::size_t skip_key_length = counter_length + 32 + 32 + 32 + 16;
+    std::size_t skip_key_length = counter_length + 32 + 32;
     std::size_t pickle_length = 3 * counter_length + 32;
     pickle_length += sender_chain.size() * send_chain_length;
     pickle_length += receiver_chains.size() * recv_chain_length;
@@ -265,10 +265,10 @@ std::uint8_t * unpickle_bytes(
 
 
 std::size_t axolotl::Ratchet::pickle(
-    std::uint8_t * output, std::size_t max_output_length
+    std::uint8_t * output, std::size_t output_length
 ) {
     std::uint8_t * pos = output;
-    if (max_output_length < pickle_max_output_length()) {
+    if (output_length < pickle_length()) {
         last_error = axolotl::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
         return std::size_t(-1);
     }