diff --git a/docs/source/api.rst b/docs/source/api.rst
index 018c76d3b..020a7e887 100644
--- a/docs/source/api.rst
+++ b/docs/source/api.rst
@@ -417,7 +417,7 @@ Will result in:
 Sessions & Transactions
 ***********************
 All database activity is co-ordinated through two mechanisms:
-**sessions** (:class:`neo4j.AsyncSession`) and **transactions**
+**sessions** (:class:`neo4j.Session`) and **transactions**
 (:class:`neo4j.Transaction`, :class:`neo4j.ManagedTransaction`).
 
 A **session** is a logical container for any number of causally-related transactional units of work.
@@ -1263,18 +1263,7 @@ Neo4j Execution Errors
 
 
 .. autoclass:: neo4j.exceptions.Neo4jError
-
-    .. autoproperty:: message
-
-    .. autoproperty:: code
-
-    There are many Neo4j status codes, see `status code `_.
-
-    .. autoproperty:: classification
-
-    .. autoproperty:: category
-
-    .. autoproperty:: title
+    :members: message, code, is_retriable
 
 
 .. autoclass:: neo4j.exceptions.ClientError
@@ -1332,7 +1321,7 @@ Connectivity Errors
 
 
 .. autoclass:: neo4j.exceptions.DriverError
-
+    :members: is_retriable
 
 .. autoclass:: neo4j.exceptions.TransactionError
     :show-inheritance:
diff --git a/neo4j/__init__.py b/neo4j/__init__.py
index f683775fc..6d5171a91 100644
--- a/neo4j/__init__.py
+++ b/neo4j/__init__.py
@@ -39,6 +39,7 @@
     "DEFAULT_DATABASE",
     "Driver",
     "ExperimentalWarning",
+    "get_user_agent",
     "GraphDatabase",
     "IPv4Address",
     "IPv6Address",
diff --git a/neo4j/_async/work/session.py b/neo4j/_async/work/session.py
index 9cb54fed6..b77fea09a 100644
--- a/neo4j/_async/work/session.py
+++ b/neo4j/_async/work/session.py
@@ -30,12 +30,11 @@
 from ...data import DataHydrator
 from ...exceptions import (
     ClientError,
-    IncompleteCommit,
+    DriverError,
     Neo4jError,
     ServiceUnavailable,
     SessionExpired,
     TransactionError,
-    TransientError,
 )
 from ...meta import (
     deprecated,
@@ -328,8 +327,9 @@ async def _transaction_error_handler(self, _):
             self._transaction = None
             await self._disconnect()
 
-    async def _open_transaction(self, *, tx_cls, access_mode, metadata=None,
-                          timeout=None):
+    async def _open_transaction(
+        self, *, tx_cls, access_mode, metadata=None, timeout=None
+    ):
         await self._connect(access_mode=access_mode)
         self._transaction = tx_cls(
             self._connection, self._config.fetch_size,
@@ -393,7 +393,11 @@ async def _run_transaction(
         metadata = getattr(transaction_function, "metadata", None)
         timeout = getattr(transaction_function, "timeout", None)
 
-        retry_delay = retry_delay_generator(self._config.initial_retry_delay, self._config.retry_delay_multiplier, self._config.retry_delay_jitter_factor)
+        retry_delay = retry_delay_generator(
+            self._config.initial_retry_delay,
+            self._config.retry_delay_multiplier,
+            self._config.retry_delay_jitter_factor
+        )
 
         errors = []
 
@@ -414,24 +418,22 @@ async def _run_transaction(
                     raise
                 else:
                     await tx._commit()
-            except IncompleteCommit:
-                raise
-            except (ServiceUnavailable, SessionExpired) as error:
-                errors.append(error)
+            except (DriverError, Neo4jError) as error:
                 await self._disconnect()
-            except TransientError as transient_error:
-                if not transient_error.is_retriable():
+                if not error.is_retriable():
                     raise
-                errors.append(transient_error)
+                errors.append(error)
             else:
                 return result
             if t0 == -1:
-                t0 = perf_counter()  # The timer should be started after the first attempt
+                # The timer should be started after the first attempt
+                t0 = perf_counter()
             t1 = perf_counter()
             if t1 - t0 > self._config.max_transaction_retry_time:
                 break
             delay = next(retry_delay)
-            log.warning("Transaction failed and will be retried in {}s ({})".format(delay, "; ".join(errors[-1].args)))
+            log.warning("Transaction failed and will be retried in {}s ({})"
+                        "".format(delay, "; ".join(errors[-1].args)))
             await async_sleep(delay)
 
         if errors:
diff --git a/neo4j/_sync/work/session.py b/neo4j/_sync/work/session.py
index fd2f99cc4..9ae88ca36 100644
--- a/neo4j/_sync/work/session.py
+++ b/neo4j/_sync/work/session.py
@@ -30,12 +30,11 @@
 from ...data import DataHydrator
 from ...exceptions import (
     ClientError,
-    IncompleteCommit,
+    DriverError,
     Neo4jError,
     ServiceUnavailable,
     SessionExpired,
     TransactionError,
-    TransientError,
 )
 from ...meta import (
     deprecated,
@@ -328,8 +327,9 @@ def _transaction_error_handler(self, _):
             self._transaction = None
             self._disconnect()
 
-    def _open_transaction(self, *, tx_cls, access_mode, metadata=None,
-                          timeout=None):
+    def _open_transaction(
+        self, *, tx_cls, access_mode, metadata=None, timeout=None
+    ):
         self._connect(access_mode=access_mode)
         self._transaction = tx_cls(
             self._connection, self._config.fetch_size,
@@ -393,7 +393,11 @@ def _run_transaction(
         metadata = getattr(transaction_function, "metadata", None)
         timeout = getattr(transaction_function, "timeout", None)
 
-        retry_delay = retry_delay_generator(self._config.initial_retry_delay, self._config.retry_delay_multiplier, self._config.retry_delay_jitter_factor)
+        retry_delay = retry_delay_generator(
+            self._config.initial_retry_delay,
+            self._config.retry_delay_multiplier,
+            self._config.retry_delay_jitter_factor
+        )
 
         errors = []
 
@@ -414,24 +418,22 @@ def _run_transaction(
                     raise
                 else:
                     tx._commit()
-            except IncompleteCommit:
-                raise
-            except (ServiceUnavailable, SessionExpired) as error:
-                errors.append(error)
+            except (DriverError, Neo4jError) as error:
                 self._disconnect()
-            except TransientError as transient_error:
-                if not transient_error.is_retriable():
+                if not error.is_retriable():
                     raise
-                errors.append(transient_error)
+                errors.append(error)
             else:
                 return result
             if t0 == -1:
-                t0 = perf_counter()  # The timer should be started after the first attempt
+                # The timer should be started after the first attempt
+                t0 = perf_counter()
             t1 = perf_counter()
             if t1 - t0 > self._config.max_transaction_retry_time:
                 break
             delay = next(retry_delay)
-            log.warning("Transaction failed and will be retried in {}s ({})".format(delay, "; ".join(errors[-1].args)))
+            log.warning("Transaction failed and will be retried in {}s ({})"
+                        "".format(delay, "; ".join(errors[-1].args)))
             sleep(delay)
 
         if errors:
diff --git a/neo4j/exceptions.py b/neo4j/exceptions.py
index bafe97371..81e64d2ee 100644
--- a/neo4j/exceptions.py
+++ b/neo4j/exceptions.py
@@ -75,11 +75,16 @@ class Neo4jError(Exception):
     """ Raised when the Cypher engine returns an error to the client.
     """
 
+    #: (str or None) The error message returned by the server.
     message = None
+    #: (str or None) The error code returned by the server.
+    #: There are many Neo4j status codes, see
+    #: `status codes `_.
     code = None
     classification = None
     category = None
     title = None
+    #: (dict) Any additional information returned by the server.
     metadata = None
 
     @classmethod
@@ -126,6 +131,19 @@ def _extract_error_class(cls, classification, code):
         else:
             return cls
 
+    def is_retriable(self):
+        """Whether the error is retryable.
+
+        Indicated whether a transaction that yielded this error makes sense to
+        retry. This methods  makes mostly sense when implementing a custom
+        retry policy in conjunction with :ref:`explicit-transactions-ref`.
+
+        :return: :const:`True` if the error is retryable,
+            :const:`False` otherwise.
+        :rtype: bool
+        """
+        return False
+
     def invalidates_all_connections(self):
         return self.code == "Neo.ClientError.Security.AuthorizationExpired"
 
@@ -163,15 +181,13 @@ class TransientError(Neo4jError):
     """
 
     def is_retriable(self):
-        """These are really client errors but classification on the server is not entirely correct and they are classified as transient.
-
-        :return: True if it is a retriable TransientError, otherwise False.
-        :rtype: bool
-        """
-        return not (self.code in (
+        # Transient errors are always retriable.
+        # However, there are some errors that are misclassified by the server.
+        # They should really be ClientErrors.
+        return self.code not in (
             "Neo.TransientError.Transaction.Terminated",
             "Neo.TransientError.Transaction.LockClientStopped",
-        ))
+        )
 
 
 class DatabaseUnavailable(TransientError):
@@ -220,6 +236,7 @@ class TokenExpired(AuthError):
     A new driver instance with a fresh authentication token needs to be created.
     """
 
+
 client_errors = {
 
     # ConstraintError
@@ -266,6 +283,18 @@ class TokenExpired(AuthError):
 class DriverError(Exception):
     """ Raised when the Driver raises an error.
     """
+    def is_retriable(self):
+        """Whether the error is retryable.
+
+        Indicated whether a transaction that yielded this error makes sense to
+        retry. This methods  makes mostly sense when implementing a custom
+        retry policy in conjunction with :ref:`explicit-transactions-ref`.
+
+        :return: :const:`True` if the error is retryable,
+            :const:`False` otherwise.
+        :rtype: bool
+        """
+        return False
 
 
 class SessionExpired(DriverError):
@@ -276,6 +305,9 @@ class SessionExpired(DriverError):
     def __init__(self, session, *args, **kwargs):
         super(SessionExpired, self).__init__(session, *args, **kwargs)
 
+    def is_retriable(self):
+        return True
+
 
 class TransactionError(DriverError):
     """ Raised when an error occurs while using a transaction.
@@ -315,6 +347,9 @@ class ServiceUnavailable(DriverError):
     """ Raised when no database service is available.
     """
 
+    def is_retriable(self):
+        return True
+
 
 class RoutingServiceUnavailable(ServiceUnavailable):
     """ Raised when no routing service is available.
@@ -340,6 +375,9 @@ class IncompleteCommit(ServiceUnavailable):
     successfully or not.
     """
 
+    def is_retriable(self):
+        return False
+
 
 class ConfigurationError(DriverError):
     """ Raised when there is an error concerning a configuration.