diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,18 @@
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.7.1.0] - 2021-09-06
+
+### Added
+
+ - Notification settings
+ - Email info
+ - State locks: opt-in locks to be used in conjunction with state files. Use of locks is not enforced.
+
+### Changed
+
+ - `DerivationOutput.outputPath` is now nullable when retrieving build info
+
 ## [0.7.0.0] - 2021-06-22
 
 ### Added
@@ -86,7 +98,8 @@
 
 Initial release
 
-[0.6.0.0]: https://github.com/hercules-ci/hercules-ci-agent/compare/hercules-ci-agent-0.6.0.1...hercules-ci-api-0.7.0.0
+[0.7.1.0]: https://github.com/hercules-ci/hercules-ci-agent/compare/hercules-ci-agent-0.7.0.0...hercules-ci-api-0.7.1.0
+[0.7.0.0]: https://github.com/hercules-ci/hercules-ci-agent/compare/hercules-ci-agent-0.6.0.1...hercules-ci-api-0.7.0.0
 [0.6.0.1]: https://github.com/hercules-ci/hercules-ci-agent/compare/hercules-ci-agent-0.6.0.0...hercules-ci-api-0.6.0.1
 [0.6.0.0]: https://github.com/hercules-ci/hercules-ci-agent/compare/hercules-ci-agent-0.5.0.0...hercules-ci-api-0.6.0.0
 [0.5.0.0]: https://github.com/hercules-ci/hercules-ci-agent/compare/hercules-ci-agent-0.4.0.0...hercules-ci-api-0.5.0.0
diff --git a/hercules-ci-api.cabal b/hercules-ci-api.cabal
--- a/hercules-ci-api.cabal
+++ b/hercules-ci-api.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               hercules-ci-api
-version:            0.7.0.0
+version:            0.7.1.0
 synopsis:           Hercules CI API definition with Servant
 homepage:           https://github.com/hercules-ci/hercules-ci-agent#readme
 bug-reports:        https://github.com/hercules-ci/hercules-ci-agent/issues
@@ -29,6 +29,9 @@
     Hercules.API.Accounts.CLIAuthorizationRequestStatus
     Hercules.API.Accounts.CLIToken
     Hercules.API.Accounts.CLITokensResponse
+    Hercules.API.Accounts.NotificationSettings
+    Hercules.API.Accounts.NotificationSettingsPatch
+    Hercules.API.Accounts.SimpleAccount
     Hercules.API.Agents
     Hercules.API.Agents.AgentInfo
     Hercules.API.Agents.AgentSession
@@ -77,10 +80,15 @@
     Hercules.API.Repos.RepoKey
     Hercules.API.Result
     Hercules.API.Servant.Status
+    Hercules.API.SourceHostingSite.SimpleSite
     Hercules.API.SourceHostingSite.SourceHostingSite
     Hercules.API.State
     Hercules.API.State.ProjectState
     Hercules.API.State.StateFile
+    Hercules.API.State.StateLockAcquireRequest
+    Hercules.API.State.StateLockAcquireResponse
+    Hercules.API.State.StateLockLease
+    Hercules.API.State.StateLockUpdateRequest
     Hercules.API.State.StateVersion
     Hercules.Frontend
 
diff --git a/src/Hercules/API/Accounts.hs b/src/Hercules/API/Accounts.hs
--- a/src/Hercules/API/Accounts.hs
+++ b/src/Hercules/API/Accounts.hs
@@ -13,6 +13,8 @@
 import Hercules.API.Accounts.CLIAuthorizationRequestCreateResponse (CLIAuthorizationRequestCreateResponse)
 import Hercules.API.Accounts.CLIAuthorizationRequestStatus (CLIAuthorizationRequestStatus)
 import Hercules.API.Accounts.CLITokensResponse (CLITokensResponse)
+import Hercules.API.Accounts.NotificationSettings (NotificationSettings)
+import Hercules.API.Accounts.NotificationSettingsPatch (NotificationSettingsPatch)
 import Hercules.API.Prelude hiding (id)
 import Hercules.API.SourceHostingSite.SourceHostingSite
   ( SourceHostingSite,
@@ -53,6 +55,25 @@
   { accountByAuth ::
       f
         :- Substitute ("accounts" :> "me" :> Placeholder) (ToServantApi (AccountResourceGroup auth)),
+    accountByAuthGetNotificationSettings ::
+      f
+        :- Summary "Retrieve notification settings"
+        :> "accounts"
+        :> "me"
+        :> "settings"
+        :> "notifications"
+        :> auth
+        :> Get '[JSON] NotificationSettings,
+    accountByAuthPatchNotificationSettings ::
+      f
+        :- Summary "Update notification settings"
+        :> "accounts"
+        :> "me"
+        :> "settings"
+        :> "notifications"
+        :> ReqBody '[JSON] NotificationSettingsPatch
+        :> auth
+        :> Patch '[JSON] NotificationSettings,
     accountById ::
       f
         :- Substitute
diff --git a/src/Hercules/API/Accounts/NotificationSettings.hs b/src/Hercules/API/Accounts/NotificationSettings.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/Accounts/NotificationSettings.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.Accounts.NotificationSettings where
+
+import Hercules.API.Accounts.SimpleAccount (SimpleAccount)
+import Hercules.API.Prelude
+import Hercules.API.SourceHostingSite.SimpleSite (SimpleSite)
+
+data NotificationLevel
+  = Ignore
+  | All
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+
+data NotificationSetting = NotificationSetting
+  { notificationLevel :: Maybe NotificationLevel,
+    notificationEmail :: Maybe Text
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+
+data NotificationAccountOverride = NotificationSettingsOverride
+  { account :: SimpleAccount,
+    setting :: NotificationSetting
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+
+data AuthorizedEmail = AuthorizedEmail
+  { address :: Text,
+    isPrimary :: Bool,
+    source :: Maybe SimpleSite
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+
+data NotificationSettings = NotificationSettings
+  { authorizedEmails :: [AuthorizedEmail],
+    defaultSetting :: Maybe NotificationSetting,
+    accountOverrides :: [NotificationAccountOverride]
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/NotificationSettingsPatch.hs b/src/Hercules/API/Accounts/NotificationSettingsPatch.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/Accounts/NotificationSettingsPatch.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.Accounts.NotificationSettingsPatch where
+
+import Hercules.API.Accounts.Account (Account)
+import Hercules.API.Accounts.NotificationSettings (NotificationSetting)
+import Hercules.API.Prelude
+
+data NotificationSettingsPatch = NotificationSettingsPatch
+  { defaultSetting :: Maybe NotificationSetting,
+    accountOverrides :: Map (Id Account) NotificationSetting
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/SimpleAccount.hs b/src/Hercules/API/Accounts/SimpleAccount.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/Accounts/SimpleAccount.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.Accounts.SimpleAccount where
+
+import Hercules.API.Accounts.Account (Account, AccountType)
+import Hercules.API.Prelude
+import Hercules.API.SourceHostingSite.SimpleSite (SimpleSite)
+
+data SimpleAccount = SimpleAccount
+  { id :: Id Account,
+    name :: Text,
+    displayName :: Text,
+    typ :: AccountType,
+    imageURL :: Text,
+    site :: SimpleSite
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs b/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs
--- a/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs
+++ b/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs
@@ -6,6 +6,6 @@
 
 data DerivationOutput = DerivationOutput
   { outputName :: Text,
-    outputPath :: Text
+    outputPath :: Maybe Text
   }
   deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/Project.hs b/src/Hercules/API/Projects/Project.hs
--- a/src/Hercules/API/Projects/Project.hs
+++ b/src/Hercules/API/Projects/Project.hs
@@ -15,6 +15,7 @@
     repoId :: Id Repo,
     enabled :: Bool,
     siteSlug :: Name SourceHostingSite,
+    ownerSlug :: Name Account,
     slug :: Name Project,
     displayName :: Text,
     imageURL :: Maybe Text,
diff --git a/src/Hercules/API/SourceHostingSite/SimpleSite.hs b/src/Hercules/API/SourceHostingSite/SimpleSite.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/SourceHostingSite/SimpleSite.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.SourceHostingSite.SimpleSite where
+
+import Hercules.API.Prelude
+import Hercules.API.SourceHostingSite.SourceHostingSite (SourceHostingSite)
+
+data SimpleSite = SimpleSite
+  { id :: Id SourceHostingSite,
+    name :: Name SourceHostingSite,
+    displayName :: Text
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State.hs b/src/Hercules/API/State.hs
--- a/src/Hercules/API/State.hs
+++ b/src/Hercules/API/State.hs
@@ -12,6 +12,9 @@
 import Hercules.API.Projects.Project (Project)
 import Hercules.API.SourceHostingSite.SourceHostingSite (SourceHostingSite)
 import Hercules.API.State.ProjectState (ProjectState)
+import Hercules.API.State.StateLockAcquireRequest (StateLockAcquireRequest)
+import Hercules.API.State.StateLockAcquireResponse (StateLockAcquireResponse, StateLockAcquiredResponse)
+import Hercules.API.State.StateLockUpdateRequest (StateLockUpdateRequest)
 import Servant.API
 import Servant.API.Generic
 
@@ -47,7 +50,14 @@
         :> "data"
         :> QueryParam' '[Optional, Strict] "version" Int
         :> auth
-        :> StreamGet NoFraming OctetStream (Headers '[ContentLength, ContentDisposition] (SourceIO RawBytes))
+        :> StreamGet NoFraming OctetStream (Headers '[ContentLength, ContentDisposition] (SourceIO RawBytes)),
+    acquireLock ::
+      f :- Summary "Acquire a lock"
+        :> "lock"
+        :> Capture' '[Required, Strict] "lockName" Text
+        :> ReqBody '[JSON] StateLockAcquireRequest
+        :> auth
+        :> Post '[JSON] StateLockAcquireResponse
   }
   deriving (Generic)
 
@@ -71,6 +81,17 @@
                  :> Capture' '[Required, Strict] "project" (Name Project)
                  :> Placeholder
              )
-             (ToServantApi (ProjectStateResourceGroup auth))
+             (ToServantApi (ProjectStateResourceGroup auth)),
+    updateLockLease ::
+      f :- "lock-leases"
+        :> Capture' '[Required, Strict] "lockLeaseId" (Id "StateLockLease")
+        :> ReqBody '[JSON] StateLockUpdateRequest
+        :> auth
+        :> Post '[JSON] StateLockAcquiredResponse,
+    deleteLockLease ::
+      f :- "lock-leases"
+        :> Capture' '[Required, Strict] "lockLeaseId" (Id "StateLockLease")
+        :> auth
+        :> Delete '[JSON] NoContent
   }
   deriving (Generic)
diff --git a/src/Hercules/API/State/StateLockAcquireRequest.hs b/src/Hercules/API/State/StateLockAcquireRequest.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/State/StateLockAcquireRequest.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.State.StateLockAcquireRequest where
+
+import Hercules.API.Prelude
+
+data StateLockAcquireRequest = StateLockAcquireRequest
+  { -- | A description of the activity that the lock is for. This may appear in
+    -- logs when other clients are blocked.
+    description :: Text,
+    -- | @True@ to request an exclusive lock. Non-exclusive locks are only mutually exclusive with exclusive locks.
+    exclusive :: Bool,
+    -- | For recursive locking. Set this to the value of environment variable
+    -- HERCULES_CI_LOCK_LEASE_ID when present.
+    parent :: Maybe (Id "StateLockLease"),
+    -- | Generate a random key to make sure that a retry can be successful.
+    idempotencyKey :: Maybe (Id "IdempotencyKey")
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateLockAcquireResponse.hs b/src/Hercules/API/State/StateLockAcquireResponse.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/State/StateLockAcquireResponse.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.State.StateLockAcquireResponse where
+
+import Hercules.API.Prelude
+import Hercules.API.State.StateLockLease (StateLockLease)
+
+data StateLockAcquireResponse
+  = Acquired StateLockAcquiredResponse
+  | Blocked StateLockBlockedResponse
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+
+data StateLockAcquiredResponse = StateLockAcquiredResponse
+  { leaseId :: Id "StateLockLease",
+    expirationTime :: UTCTime
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+
+data StateLockBlockedResponse = LockBlockedResponse
+  { blockedByLeases :: [StateLockLease]
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateLockLease.hs b/src/Hercules/API/State/StateLockLease.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/State/StateLockLease.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.State.StateLockLease where
+
+import Hercules.API.Accounts.SimpleAccount (SimpleAccount)
+import Hercules.API.Prelude
+import Hercules.API.Projects.SimpleJob (SimpleJob)
+
+data StateLockLease = StateLockLease
+  { id :: Id "StateLockLease",
+    -- | When the lease was granted.
+    startTime :: UTCTime,
+    -- | Time of expiry. The lease can be terminated or extended by the actor.
+    expirationTime :: UTCTime,
+    -- | In case the lock was acquired by a user.
+    user :: Maybe SimpleAccount,
+    -- | In case the lock was acquired by an effect.
+    job :: Maybe SimpleJob,
+    -- | User-provided text describing the reason to lock. May be empty.
+    description :: Text,
+    -- | A pre-existing lock lease. This allows a lease to be granted when the
+    -- actor knows it already has a lock lease.
+    parent :: Maybe (Id "StateLockLease"),
+    -- | Whether the lock is exclusive.
+    exclusive :: Bool
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateLockUpdateRequest.hs b/src/Hercules/API/State/StateLockUpdateRequest.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/State/StateLockUpdateRequest.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
+
+module Hercules.API.State.StateLockUpdateRequest where
+
+import Hercules.API.Prelude
+
+data StateLockUpdateRequest = StateLockUpdateRequest
+  { description :: Maybe Text
+  }
+  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/Frontend.hs b/src/Hercules/Frontend.hs
--- a/src/Hercules/Frontend.hs
+++ b/src/Hercules/Frontend.hs
@@ -40,7 +40,10 @@
         :> Capture' [Required, Strict] "project" (Name Project)
         :> "jobs"
         :> Capture' [Required, Strict] "jobIndex" Int
-        :> view
+        :> view,
+    notificationSettings ::
+      f
+        :- "settings" :> "notifications" :> view
   }
   deriving (Generic)
 
