     )
   end
 
-  @spec send_pdu(pid, Pdu.t) :: :ok
-
+  @spec send_pdu(esme :: pid, pdu :: Pdu.t) :: :ok
+
+  @doc """
+  Sends outcoming PDU from the ESME.
+
+  The whole command is sent to the ESME asyncronously. The further lifecycle of the PDU
+  can be traced through callbacks.
+  """
   def send_pdu(esme, pdu) do
     GenServer.cast(esme, {:send_pdu, pdu})
   end
 
-  @spec reply(pid, Pdu.t, Pdu.t) :: :ok
-
+  @spec reply(esme :: pid, pdu :: Pdu.t, reply_pdu :: Pdu.t) :: :ok
+
+  @doc """
+  Sends reply to previously received PDU from the ESME.
+
+  The whole command is sent to the ESME asyncronously. The further lifecycle of the response PDU
+  can be traced through callbacks.
+  """
   def reply(esme, pdu, reply_pdu) do
     GenServer.cast(esme, {:reply, pdu, reply_pdu})
   end
 
-  @spec stop(pid) :: :ok
-
+  @spec stop(esme :: pid) :: :ok
+
+  @doc """
+  Stops ESME asyncronously.
+
+  The very moment of the SMPP session termination can be traced via `handle_stop` callback.
+  """
   def stop(esme) do
     GenServer.cast(esme, :stop)
   end
 
+  @spec call(esme ::pid, arg :: term, timeout) :: term
+
+  @doc """
+  Makes a syncronous call to ESME.
+
+  The call is handled by `handle_call/3` ESME callback.
+  """
+  def call(esme, request, timeout \\ @default_call_timeout) do
+    GenServer.call(esme, {:call, request}, timeout)
+  end
+
+  @spec cast(pid, term) :: :ok
+
+  @doc """
+  Makes an asyncronous call to ESME.
+
+  The call is handled by `handle_cast/2` ESME callback.
+  """
+  def cast(esme, request) do
+    GenServer.cast(esme, {:cast, request})
+  end
+
+  @spec with_session(esme :: pid, (smpp_session :: pid -> any)) :: :ok
+
+  @doc """
+  Asyncronously executes the passed lambda passing SMPP session to it directly.
+
+  This function can be used for uncommon cases like sending PDUs bypassing timers or
+  sequence_number assignment.
+  """
+  def with_session(esme, fun) do
+    GenServer.cast(esme, {:with_session, fun})
+  end
+
   @spec handle_pdu(pid, Pdu.t) :: :ok
 
+  @doc false
   def handle_pdu(esme, pdu) do
     GenServer.call(esme, {:handle_pdu, pdu})
   end
 
   @spec handle_stop(pid) :: :ok
 
+  @doc false
   def handle_stop(esme) do
     GenServer.call(esme, :handle_stop)
   end

  @type send_pdu_result :: :ok | {:error, term}
  @spec handle_send_pdu_result(pid, Pdu.t, send_pdu_result) :: :ok

  def handle_send_pdu_result(esme, pdu, send_pdu_result) do
    GenServer.call(esme, {:handle_send_pdu_result, pdu, send_pdu_result})
  end

  @spec call(pid, term, timeout) :: term

  def call(esme, request, timeout \\ @default_call_timeout) do
    GenServer.call(esme, {:call, request}, timeout)
  end

  @spec cast(pid, term) :: :ok

  def cast(esme, request) do
    GenServer.cast(esme, {:cast, request})
  end

  @spec with_session(pid, (pid -> any)) :: :ok

  def with_session(esme, fun) do
    GenServer.cast(esme, {:with_session, fun})
  end

  # GenServer callbacks

  def init([host, port, mod_with_args, transport, timeout, esme_opts]) do
    esme = self
    handler = fn(ref, _socket, _transport, session) ->

    end
  end

  @doc false
  def handle_call({:handle_pdu, pdu}, _from, st) do
    case Pdu.resp?(pdu) do
      true -> do_handle_resp(pdu, st)
