diff --git a/cachix-api.cabal b/cachix-api.cabal
--- a/cachix-api.cabal
+++ b/cachix-api.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               cachix-api
-version:            1.2
+version:            1.3
 synopsis:           Servant HTTP API specification for https://cachix.org
 homepage:           https://github.com/cachix/cachix#readme
 bug-reports:        https://github.com/cachix/cachix/issues
@@ -52,6 +52,7 @@
     Cachix.Types.DeployResponse
     Cachix.Types.DeployResponse.V1
     Cachix.Types.DeployResponse.V2
+    Cachix.Types.MultipartUpload
     Cachix.Types.NarFileName
     Cachix.Types.NarInfo
     Cachix.Types.NarInfoCreate
diff --git a/src/Cachix/API.hs b/src/Cachix/API.hs
--- a/src/Cachix/API.hs
+++ b/src/Cachix/API.hs
@@ -12,16 +12,18 @@
 import qualified Cachix.Types.BinaryCache as BinaryCache
 import qualified Cachix.Types.ByteStringStreaming as ByteStringStreaming
 import Cachix.Types.ContentTypes
+import qualified Cachix.Types.MultipartUpload as Multipart
 import Cachix.Types.NarFileName (NarFileName (..))
 import qualified Cachix.Types.NarInfo as NarInfo
 import qualified Cachix.Types.NarInfoCreate as NarInfoCreate
 import qualified Cachix.Types.NarInfoHash as NarInfoHash
 import qualified Cachix.Types.NixCacheInfo as NixCacheInfo
-import Cachix.Types.Servant (Head)
+import Cachix.Types.Servant (Get302, Head)
 import Cachix.Types.Session (Session)
 import qualified Cachix.Types.SigningKeyCreate as SigningKeyCreate
 import Control.Monad.Trans.Resource
 import Data.Conduit (ConduitT)
+import Data.UUID (UUID)
 import Protolude
 import Servant.API hiding (BasicAuth)
 import Servant.API.Generic
@@ -62,7 +64,7 @@
         :> Capture "name" Text
         :> "nar"
         :> Capture "nar" NarFileName
-        :> StreamGet NoFraming XNixNar (ConduitT () ByteStringStreaming.ByteStringStreaming (ResourceT IO) ()),
+        :> Get302 '[XNixNar] '[],
     -- cachix specific
     getCache ::
       route
@@ -87,22 +89,69 @@
         :> "narurl"
         :> Capture "nar" NarFileName
         :> Get '[JSON] Text,
-    createNarinfo ::
+    createAndUploadNar ::
       route
-        :- CachixAuth
+        :- Summary "Upload a NAR directly to the Cachix Server"
+        :> Description "This is a legacy API for older Cachix clients. Prefer 'createNar' instead."
+        :> CachixAuth
         :> "cache"
         :> Capture "name" Text
-        :> Capture "narinfohash" NarInfoHash.NarInfoHash
-        :> ReqBody '[JSON] NarInfoCreate.NarInfoCreate
+        :> "nar"
+        :> QueryParam "compression" BinaryCache.CompressionMethod
+        :> StreamBody NoFraming XNixNar (ConduitT () ByteStringStreaming.ByteStringStreaming (ResourceT IO) ())
         :> Post '[JSON] NoContent,
     createNar ::
       route
-        :- CachixAuth
+        :- Summary "Create an empty NAR and initiate a multipart upload"
+        :> CachixAuth
         :> "cache"
         :> Capture "name" Text
-        :> "nar"
+        :> "multipart-nar"
         :> QueryParam "compression" BinaryCache.CompressionMethod
-        :> StreamBody NoFraming XNixNar (ConduitT () ByteStringStreaming.ByteStringStreaming (ResourceT IO) ())
+        :> Post '[JSON] Multipart.CreateMultipartUploadResponse,
+    uploadNarPart ::
+      route
+        :- Summary "Retrieve a presigned URL to upload a part of a multipart NAR"
+        :> CachixAuth
+        :> "cache"
+        :> Capture "name" Text
+        :> "multipart-nar"
+        :> Capture "narUuid" UUID
+        :> QueryParam' '[Required] "uploadId" Text
+        :> QueryParam' '[Required] "partNumber" Int
+        :> ReqBody '[JSON] Multipart.SigningData
+        :> Post '[JSON] Multipart.UploadPartResponse,
+    completeNarUpload ::
+      route
+        :- Summary "Complete a multipart upload"
+        :> Description "Verify the etags for each part and create the narinfo"
+        :> CachixAuth
+        :> "cache"
+        :> Capture "name" Text
+        :> "multipart-nar"
+        :> Capture "narUuid" UUID
+        :> "complete"
+        :> QueryParam' '[Required] "uploadId" Text
+        :> ReqBody '[JSON] Multipart.CompletedMultipartUpload
+        :> Post '[JSON] NoContent,
+    abortMultipartUpload ::
+      route
+        :- Summary "Abort a multipart upload"
+        :> CachixAuth
+        :> "cache"
+        :> Capture "name" Text
+        :> "multipart-nar"
+        :> Capture "narUuid" UUID
+        :> "abort"
+        :> QueryParam' '[Required] "uploadId" Text
+        :> Post '[JSON] NoContent,
+    createNarinfo ::
+      route
+        :- CachixAuth
+        :> "cache"
+        :> Capture "name" Text
+        :> Capture "narinfohash" NarInfoHash.NarInfoHash
+        :> ReqBody '[JSON] NarInfoCreate.NarInfoCreate
         :> Post '[JSON] NoContent,
     serveNarContent ::
       route
diff --git a/src/Cachix/Types/MultipartUpload.hs b/src/Cachix/Types/MultipartUpload.hs
new file mode 100644
--- /dev/null
+++ b/src/Cachix/Types/MultipartUpload.hs
@@ -0,0 +1,44 @@
+module Cachix.Types.MultipartUpload where
+
+import Cachix.Types.NarInfoCreate (NarInfoCreate)
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Swagger (ToSchema)
+import Data.UUID (UUID)
+import Protolude
+
+data CreateMultipartUploadResponse = CreateMultipartUploadResponse
+  { narId :: UUID,
+    uploadId :: Text
+  }
+  deriving stock (Generic, Show)
+  deriving anyclass (ToJSON, FromJSON, ToSchema, NFData)
+
+-- | Any hashes or headers required to create the presigned URL.
+data SigningData = SigningData
+  { contentMD5 :: Text
+  }
+  deriving stock (Generic, Show)
+  deriving anyclass (ToJSON, FromJSON, ToSchema, NFData)
+
+newtype UploadPartResponse = UploadPartResponse {uploadUrl :: Text}
+  deriving stock (Generic, Show)
+  deriving anyclass (ToJSON, FromJSON, ToSchema, NFData)
+
+data CompletedPart = CompletedPart
+  { partNumber :: Int,
+    -- | An opaque identifier for the uploaded part.
+    eTag :: Text
+  }
+  deriving stock (Generic, Show)
+  deriving anyclass (ToJSON, FromJSON, ToSchema, NFData)
+
+type CompletedParts = Maybe (NonEmpty CompletedPart)
+
+data CompletedMultipartUpload = CompletedMultipartUpload
+  { -- | A list of 'CompletedPart`, sorted by the 'partNumber'.
+    parts :: CompletedParts,
+    -- | The narinfo to create after verifying the upload.
+    narInfoCreate :: NarInfoCreate
+  }
+  deriving stock (Generic, Show)
+  deriving anyclass (ToJSON, FromJSON, ToSchema)
